feat: Implement Uniform Buffers Object Support (#3360)
This PR implements UBO support in Excalibur
```typescript
var material = game.graphicsContext.createMaterial({
name: 'light-material',
fragmentSource: `#version 300 es
precision mediump float;
struct Light {
vec2 pos;
float radius;
vec4 color;
};
layout(std140) uniform Lighting {
Light lights[2];
};
in vec2 v_uv;
out vec4 color;
void main() {
float distanceToLights = 1.0;
vec4 finalColor = vec4(0.0, 0.0, 0.0, 1.0);
for (int i = 0; i < 2; i++) {
float dist = length(lights[i].pos - v_uv);
dist = smoothstep(lights[i].radius-.2, lights[i].radius+.2, dist);
finalColor += lights[i].color * (1.0 - dist);
}
color = finalColor;
// premultiply alpha
color.rgb = color.rgb * color.a;
}`,
uniforms: {
// prettier-ignore
Lighting: new Float32Array([
0.5, 0.5, 0.1, 0.1, // light 1 pos
0, 1, 0, 1, // light 1 color
1, 1, 0.1, 0.1, // light2 pos
0, 0, 1, 1 // light 2 color
])
}
}) as ex.Material;
actor.graphics.material = material;
ex.coroutine(
game,
function* () {
let time = 0;
while (true) {
const elapsed = yield;
time += elapsed / 1000;
const x1 = Math.cos(time);
const y1 = Math.sin(time);
const x2 = Math.cos(-time);
const y2 = Math.sin(-time);
// prettier-ignore
material.uniforms.Lighting = new Float32Array([
0.2 * x1 + 0.2, 0.2 * y1 + 0.2, 0.1, 0,
0, 1, 0, 1,
0.5 * x2 + 0.5, 0.5 * y2 + 0.5, 0.1, 0,
0, 0, 1, 1
]); }
}.bind(this)
);
game.start(new ex.Loader([tex]));
```
Additionally there are some DX enhancements to working with uniforms
- You can now set uniform values directly on materials/shader instances and will be upload on next `.use()`
`material.uniforms.Lighting = ...`