WebGL canvas in React
Tags: webgl, typescript, beginners, react
Published: 2023-01-20 17:28:11

It's a very fun time every time I start a new WebGL project. I keep forgetting about the basic setup, so I always end up looking up on Google for tutorials on drawing a basic triangle.

Well no! It's not fun at all!

Lately, I started to be a bit smarter about it and create a template I can reuse, so I don't have to relearn everything from scratch every time.

For those who also need to start a new WebGL project and are looking for a Hello World, here's a neat template that sets up the WebGL basic.

https://github.com/jacklehamster/opengl-template

Along the way, let's go through the step for setting up a simple WebGL hello world. We're also here to learn!

- [Define the WebGL Canvas](https://github.com/jacklehamster/opengl-template/blob/main/src/GLCanvas.tsx#L42-L50), and get a ref from it.
```typescript
<canvas ref={canvasRef}
            width={width}
            height={height}
            style={{
                ...props?.style,
                width: "100%",
                height: "100%",
            }}>
</canvas>;
```

- [Set the WebGL2RenderingContext context](https://github.com/jacklehamster/opengl-template/blob/main/src/gl/use-gl.ts), passing some 
WebGL attributes if needed.
```typescript
const [gl, setGL] = useState<WebGL2RenderingContext | undefined>();

React.useLayoutEffect(() => {
   const canvas = canvasRef.current;
   setGL(canvas?.getContext?.("webgl2", {
      ...DEFAULT_ATTRIBUTES,
      ...webglAttributes,
   }) ?? undefined);
}, []);
```

- [Attach the vertex and fragment shaders](https://github.com/jacklehamster/opengl-template/blob/main/src/gl/use-shader.ts#L54-L62). Make sure to do proper cleanup.
```typescript
const vertexShader = createShader(vertex, gl.VERTEX_SHADER)!;
const fragmentShader = createShader(fragment, gl.FRAGMENT_SHADER)!;
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.detachShader(program, vertexShader);
gl.detachShader(program, fragmentShader);
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
```

The template does all of the above, provided that you pass the shader codes. You can then write custom code to render cool stuff in WebGL. For now let's just render a triangle.

- [Setup the Vertex and Fragment shaders](https://github.com/jacklehamster/opengl-template). In the vertex shader, we just grab the position and color and pass it further. In fragment, we just grab the color and process it.

Vertex:
```glsl
#version 300 es

precision highp float;
layout (location=0) in vec4 position;
layout (location=1) in vec3 color;
                
out vec3 vColor;

void main() {
   vColor = color;
   gl_Position = position;
}
```

Fragment:
```glsl
#version 300 es

precision highp float;
in vec3 vColor;
out vec4 fragColor;

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

- [Prepare the position and color buffers](https://github.com/jacklehamster/opengl-template/blob/main/public/index.html#L10-L39).
```javascript
const triangleArray = gl.createVertexArray();
gl.bindVertexArray(triangleArray);

const positionLocation = controller.getAttributeLocation("position");
const positions = positionLocation >= 0 ? new Float32Array([
   0.0, 0.5, 0.0,
   -0.5, -0.5, 0.0,
   0.5, -0.5, 0.0,
]) : undefined;
const positionBuffer = positionLocation >= 0 ? gl.createBuffer() : undefined;
if (positionLocation >= 0) {
   gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
   gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
   gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
   gl.enableVertexAttribArray(positionLocation);
}

const colorLocation = controller.getAttributeLocation("color");
const colors = colorLocation >= 0 ? new Float32Array([
   1.0, 0.0, 0.0,
   0.0, 1.0, 0.0,
   0.0, 0.0, 1.0
]) : undefined;
const colorBuffer = colorLocation >= 0 ? gl.createBuffer() : undefined;
if (colorLocation >= 0) {
   gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);SubtleCrypto
   gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
   gl.vertexAttribPointer(colorLocation, 3, gl.FLOAT, false, 0, 0);
   gl.enableVertexAttribArray(colorLocation);
}
```

- [Execute a draw call](https://github.com/jacklehamster/opengl-template/blob/main/public/index.html#L64-L65).

```javascript
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 3);
```

I hope this was helpful.

You can go into details by checking out the [repo](https://github.com/jacklehamster/opengl-template) and the [demo page](https://jacklehamster.github.io/opengl-template/public/).

I'll be doing a few more post with some neat WebGL tricks for beginners, such as animating within the Vertex shader so you don't have to interact with the GPU every frame.