# WebGL Simple 2D Example 13

### Speed Up by setting fewer slots in vertices[]

• We already have a working renderer for 2D games!
• The only problem is we are passing in 48 values to draw 1 image.
• Pushing 48 slots to our vertices array for each image isn't efficient. Just setting array slots could actually cause game lag.
• The main point of using this renderer instead of a regular canvas is for rendering speed.
• This optimization is a complex step.
My texture: #### Full JavaScript code on this page: (or you can View Page Source)

```var gl, glExtension, canvas, vertices = []

function drawImage(x, y, width, height, r, g, b, a, texX, texY, texWidth, texHeight) {
// UPDATE: No repeated data anymore. Now only setting 12 slots instead of 48.
vertices.push(x, y, width, height, r, g, b, a, texX, texY, texWidth, texHeight)
}

// Draw rectangle by drawing the white pixel in our PNG.
function drawRectangle(x, y, width, height, r, g, b, a) {
drawImage(x, y, width, height, r, g, b, a, 1, 1, 1, 1)
}

function gameLoop() {
window.requestAnimationFrame(gameLoop)

// Draw a wide rectangle.
drawRectangle(0,370, 200,50, 200,100,10,1)

// Draw the moving image.
var x = 250+Math.sin(Date.now()*.004)*250
var y = 200
var blue = 128 + Math.floor(Math.sin(Date.now()*.01) * 127)
var frame = (Date.now()/100)&1

drawImage(
x, y,
100, 200,
0, 255, blue, 1,
frame*32,32, 32,32
)

glRender()
}

function glRender() {
// Clear the screen.
gl.clear(gl.COLOR_BUFFER_BIT)

// UPDATE: Convert our vertices[] into array buffer format.
var arrayBuffer = new Float32Array(vertices)
gl.bufferData(gl.ARRAY_BUFFER, arrayBuffer, gl.DYNAMIC_DRAW)

// Tell webGL to draw these vertices this frame.
gl.bufferSubData(gl.ARRAY_BUFFER, 0, arrayBuffer)

// UPDATE: Draw all the images. vertices.length/12 is the amount of images we are drawing. We set 12 slots each drawImage().
// 6 is the amount of points per image since they are made of 2 triangles.
glExtension.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, vertices.length/12)

// Clear vertices. We will fill it every frame.
// This way you don't need to delete objects from the screen. You just stop drawing them.
vertices = []
}

function glSetup() {
gl = myCanvas.getContext("experimental-webgl")

// UPDATE: This extension allows us to repeat the draw vertex operation 6 times (to make 2 triangles) on the same 12 slots in
//  vertices[] so we only have to put the image data into vertices[] once for each image each time we want to draw an image.
glExtension = gl.getExtension("ANGLE_instanced_arrays")

// Set the background color to sky blue.
gl.clearColor(.5, .7, 1, 1)

var vertCode =
"attribute vec2 coordinates;" +
"attribute vec2 drawSize;" +
"attribute vec2 whichCorner;" +
"attribute vec4 rgba;" +
"attribute vec2 texPos;" +
"attribute vec2 texPartSize;" +

"varying highp vec4 rgbaForFrag;" +
"varying highp vec2 texPosForFrag;" +
"uniform vec2 canvasSize;" +
"uniform vec2 texSize;" +

"void main(void) {" +
" vec2 drawPos;" +
// UPDATE: Calculate which of the 4 corners of the image we are drawing now.
// Then divide the position by our current canvas size.
" drawPos = (coordinates + drawSize*whichCorner) / canvasSize * 2.0;" +

// We are passing in only 2D coordinates. Then Z is always 0.0 and the divisor is always 1.0
" gl_Position = vec4(drawPos.x - 1.0, 1.0 - drawPos.y, 0.0, 1.0);" +
// Pass the color and transparency to the fragment shader.
" rgbaForFrag = vec4(rgba.xyz / 255.0, rgba.w);" +
// UPDATE: Pass the texture position to the fragment shader. Now supports 4 corners, all with the same vertex data.
" texPosForFrag = (texPos + texPartSize*whichCorner) / texSize;" +
"}"

// Create a vertex shader object.

var fragCode =
"varying highp vec4 rgbaForFrag;" +
"varying highp vec2 texPosForFrag;" +
"uniform sampler2D sampler;" +
"void main(void) {" +
" gl_FragColor = texture2D(sampler, texPosForFrag) * rgbaForFrag;" +
"}"

// Tell webGL to use both my shaders.

// UPDATE: Tell webGL when drawing a triangle to access the same vertex data twice so we can pass in 4 corners of a rectangle,
//  to draw 2 triangles which would normally need 6 points worth of vertex data.
// Map triangle vertexes to our multiplier array, for which corner of the image drawn's rectangle each triangle point is at.
// This shows what order to access the gl.ARRAY_BUFFER below, so it can still make 2 triangles using those 4 points,
//  so first it will make a point at the top-left, then the bottom-left, then the top-right, then (the second triangle starts)
//  the top-right again, then bottom-left again, then bottom right (which is index 3 which refers to 1,1 in my ARRAY_BUFFER).
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer())
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([0, 1, 2, 2, 1, 3]), gl.STATIC_DRAW)

// UPDATE: Our multiplier array for width/height so we can get to each corner of the image drawn.
// index 0 has 0,0 which will be the top left of the image we are drawing because size will get multiplied to 0.
// index 1 has 0,1 which will be the bottom left of the image we are drawing since width will get *0 but height gets *1. etc.
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer())
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0,0, 0,1, 1,0, 1,1]), gl.STATIC_DRAW)

// UPDATE: whichCorner will access the array above, so we can get to each point in the rectangle image we want to draw.
//  draw size will be multiplied by this so 0,0 will be the top left of the image, and 1,1 will be the bottom right.
gl.enableVertexAttribArray(attribute)
gl.vertexAttribPointer(attribute, 2, gl.FLOAT, false, 0, 0)

// Now set a default array buffer to read our vertices[] from.
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer())

// Tell webGL to read 2 floats from the vertex array for each vertex
// and store them in my vec2 shader variable I've named "coordinates"
// We need to tell it that each vertex takes 48 bytes now (8 floats)
gl.vertexAttribPointer(attribute, 2, gl.FLOAT, false, 48, 0)
gl.enableVertexAttribArray(attribute)
// UPDATE: Need to register our attibutes with the extension now.
glExtension.vertexAttribDivisorANGLE(attribute, 1)

// UPDATE: Then read width and height from the vertex array and store them in "drawSize".
//  since "coordinates" won't be getting set to every corner of the image anymore (just top-left) we'll add drawSize to it instead.
gl.vertexAttribPointer(attribute, 2, gl.FLOAT, false, 48, 8)
gl.enableVertexAttribArray(attribute)
glExtension.vertexAttribDivisorANGLE(attribute, 1)

// Tell webGL to read 4 floats from the vertex array for each vertex
// and store them in my vec4 shader variable I've named "rgba"
// Start after 8 bytes. (After the 2 floats for x and y)
gl.vertexAttribPointer(attribute, 4, gl.FLOAT, false, 48, 16)
gl.enableVertexAttribArray(attribute)
// UPDATE: Need to register our attibutes with the extension now.
glExtension.vertexAttribDivisorANGLE(attribute, 1)

// Tell webGL to read 2 floats from the vertex array for each vertex
// and store them in my vec2 shader variable I've named "texPos"
gl.vertexAttribPointer(attribute, 2, gl.FLOAT, false, 48, 32)
gl.enableVertexAttribArray(attribute)
// UPDATE: Need to register our attibutes with the extension now.
glExtension.vertexAttribDivisorANGLE(attribute, 1)

// UPDATE: Also read 2 slots for size of the part of the texture you are drawing.
//  since "texPos" won't be getting set to every corner of the the part of our texture that we want to draw anymore (just top-left),
//  we'll add texPos+texPartSize in our shader now to get to the other corners.
gl.vertexAttribPointer(attribute, 2, gl.FLOAT, false, 48, 40)
gl.enableVertexAttribArray(attribute)
// UPDATE: Need to register our attibutes with the extension now.
glExtension.vertexAttribDivisorANGLE(attribute, 1)

// Tell webGL that when we set the opacity, it should be semi transparent above what was already drawn.
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
gl.enable(gl.BLEND)

var image = new Image()
{
// Create a gl texture from our JS image object.
gl.bindTexture(gl.TEXTURE_2D, gl.createTexture())
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image)
gl.activeTexture(gl.TEXTURE0)

// Tell gl that when draw images scaled up, smooth it.
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)

// Save texture dimensions in our shader.

}
image.src = "tiles.png"

// Call onresize to set the initial canvas size.
window.onresize()
}

window.onresize = function() {
// To test we'll make the canvas be 1/3 the browser width. Try resizing your browser.
var width = Math.floor(innerWidth/3)
var height = 500
myCanvas.style.width = width+"px"
myCanvas.style.height = height+"px"
myCanvas.setAttribute("width", width)
myCanvas.setAttribute("height", height)

// Set the viewport size to be the whole canvas.
gl.viewport(0, 0, width, height)
// Set our shader variable for canvas size. It's a vec2 that holds both width and height.
`					`