Skip to main content

Hello Triangle

This tutorial will demonstrate how to draw a triangle using luma.gl's high-level APIs.

caution

The tutorial pages have not yet been updated for luma.gl v9.

It is assumed you've set up your development environment as described in Getting Started. Your index.js file should look like the following:

import {AnimationLoop} from '@luma.gl/engine';
import {clear} from '@luma.gl/webgl';

class AppAnimationLoop extends AnimationLoop ({
override onInitialize({device}) {
// Setup logic goes here
},

override onRender({device}) {
// Drawing logic goes here
clear(device, {color: [0, 0, 0, 1]});
}
});

const loop = new AppAnimationLoop();
loop.start();

First, we'll need to update our imports with the classes we'll be using, Buffer and Model:

import {AnimationLoop, Model} from '@luma.gl/engine';
import {clear} from '@luma.gl/webgl';

Now let's create some buffers in the onInitialize method to hold our attribute data:

  override onInitialize({device}) {
// Setup logic goes here
const positionBuffer = device.createBuffer(new Float32Array([
-0.5, -0.5,
0.5, -0.5,
0.0, 0.5
]));

const colorBuffer = device.createBuffer(new Float32Array([
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0
]));
}

Next let's add the vertex and fragment shader code we'll be using to draw:

  override onInitialize({device}) {
// Setup logic goes here

// Buffers...

const vs = `
attribute vec2 position;
attribute vec3 color;

out vec3 vColor;

void main() {
vColor = color;
gl_Position = vec4(position, 0.0, 1.0);
}
`;

const fs = `
in vec3 vColor;

void main() {
gl_FragColor = vec4(vColor, 1.0);
}
`;

}

As a final step in our initialization, we'll create a Model in onInitialize:

  override onInitialize({device}) {
// Setup logic goes here

// Buffers...

// Shaders...

this.model = new Model(device, {
vs,
fs,
attributes: {
position: positionBuffer,
color: colorBuffer
},
vertexCount: 3
});

return {model};
}

A Model can be thought of as gathering all the WebGL pieces necessary for a single draw call: programs, attributes, uniforms. Also note that we return the Model instance we created. This will make it available to the onRender method.

Our onRender method is comparitavely much simpler:

  override onRender({device}) {
clear(device, {color: [0, 0, 0, 1]});
this.model.draw();
}

This clears the canvas and draws the Model. If all went well, you should see a tri-color triangle on a black background.

The entire application should look like the following:

import {AnimationLoop, Model} from '@luma.gl/engine';
import {clear} from '@luma.gl/webgl';

class AppAnimationLoop extends AnimationLoop {
override onInitialize({device}) {
const positionBuffer = device.createBuffer(new Float32Array([-0.5, -0.5, 0.5, -0.5, 0.0, 0.5]));

const colorBuffer = device.createBuffer(new Float32Array([1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]));

const vs = `
attribute vec2 position;
attribute vec3 color;

out vec3 vColor;

void main() {
vColor = color;
gl_Position = vec4(position, 0.0, 1.0);
}
`;

const fs = `
in vec3 vColor;

void main() {
gl_FragColor = vec4(vColor, 1.0);
}
`;

const model = new Model(device, {
vs,
fs,
attributes: {
position: positionBuffer,
color: colorBuffer
},
vertexCount: 3
});

return {model};
},

override onRender({device, model}) {
clear(device, {color: [0, 0, 0, 1]});
model.draw();
}
};

const loop = new AppAnimationLoop();
loop.start();