mirror of
https://github.com/visgl/luma.gl.git
synced 2025-12-08 17:36:19 +00:00
247 lines
7.7 KiB
HTML
247 lines
7.7 KiB
HTML
<html>
|
|
<head>
|
|
<style>
|
|
body {margin: 0; padding: 0;}
|
|
</style>
|
|
<script src="https://unpkg.com/@luma.gl/core/dist/dist.min.js"></script>
|
|
<script src="https://unpkg.com/@luma.gl/experimental/dist/dist.min.js"></script>
|
|
<script src="https://unpkg.com/gl-matrix@3.1.0/gl-matrix-min.js"></script>
|
|
</head>
|
|
<body>
|
|
<script>
|
|
const {mat4} = glMatrix;
|
|
|
|
const SIDE = 256;
|
|
|
|
createShaderHook('vs:MY_SHADER_HOOK_pickColor(inout vec4 color)');
|
|
|
|
createShaderHook('fs:MY_SHADER_HOOK_fragmentColor(inout vec4 color)');
|
|
|
|
createModuleInjection('picking', {
|
|
hook: 'vs:MY_SHADER_HOOK_pickColor',
|
|
injection: 'picking_setPickingColor(color.rgb);'
|
|
});
|
|
|
|
createModuleInjection('dirlight', {
|
|
hook: 'fs:MY_SHADER_HOOK_fragmentColor',
|
|
injection: 'color = dirlight_filterColor(color);'
|
|
});
|
|
|
|
createModuleInjection('picking', {
|
|
hook: 'fs:MY_SHADER_HOOK_fragmentColor',
|
|
injection: 'color = picking_filterColor(color);',
|
|
order: Number.POSITIVE_INFINITY
|
|
});
|
|
|
|
// Make a cube with 65K instances and attributes to control offset and color of each instance
|
|
class InstancedCube extends ModelNode {
|
|
constructor(gl, props) {
|
|
let offsets = [];
|
|
for (let i = 0; i < SIDE; i++) {
|
|
const x = ((-SIDE + 1) * 3) / 2 + i * 3;
|
|
for (let j = 0; j < SIDE; j++) {
|
|
const y = ((-SIDE + 1) * 3) / 2 + j * 3;
|
|
offsets.push(x, y);
|
|
}
|
|
}
|
|
offsets = new Float32Array(offsets);
|
|
|
|
const pickingColors = new Uint8ClampedArray(SIDE * SIDE * 2);
|
|
for (let i = 0; i < SIDE; i++) {
|
|
for (let j = 0; j < SIDE; j++) {
|
|
pickingColors[(i * SIDE + j) * 2 + 0] = i;
|
|
pickingColors[(i * SIDE + j) * 2 + 1] = j;
|
|
}
|
|
}
|
|
|
|
const colors = new Float32Array(SIDE * SIDE * 3).map(() => Math.random() * 0.75 + 0.25);
|
|
|
|
const vs = `\
|
|
attribute float instanceSizes;
|
|
attribute vec3 positions;
|
|
attribute vec3 normals;
|
|
attribute vec2 instanceOffsets;
|
|
attribute vec3 instanceColors;
|
|
attribute vec2 instancePickingColors;
|
|
|
|
uniform mat4 uModel;
|
|
uniform mat4 uView;
|
|
uniform mat4 uProjection;
|
|
uniform float uTime;
|
|
|
|
varying vec3 color;
|
|
|
|
void main(void) {
|
|
vec3 normal = vec3(uModel * vec4(normals, 1.0));
|
|
|
|
// Set up data for modules
|
|
color = instanceColors;
|
|
project_setNormal(normal);
|
|
vec4 pickColor = vec4(0., instancePickingColors, 1.0);
|
|
MY_SHADER_HOOK_pickColor(pickColor);
|
|
|
|
// Vertex position (z coordinate undulates with time), and model rotates around center
|
|
float delta = length(instanceOffsets);
|
|
vec4 offset = vec4(instanceOffsets, sin((uTime + delta) * 0.1) * 16.0, 0);
|
|
gl_Position = uProjection * uView * (uModel * vec4(positions * instanceSizes, 1.0) + offset);
|
|
}
|
|
`;
|
|
const fs = `\
|
|
precision highp float;
|
|
|
|
varying vec3 color;
|
|
|
|
void main(void) {
|
|
gl_FragColor = vec4(color, 1.);
|
|
MY_SHADER_HOOK_fragmentColor(gl_FragColor);
|
|
}
|
|
`;
|
|
|
|
const offsetsBuffer = new Buffer(gl, offsets);
|
|
const colorsBuffer = new Buffer(gl, colors);
|
|
const pickingColorsBuffer = new Buffer(gl, pickingColors);
|
|
|
|
super(
|
|
gl,
|
|
Object.assign({}, props, {
|
|
vs,
|
|
fs,
|
|
modules: [dirlight, picking],
|
|
isInstanced: 1,
|
|
instanceCount: SIDE * SIDE,
|
|
geometry: new CubeGeometry(),
|
|
attributes: {
|
|
instanceSizes: new Float32Array([1]), // Constant attribute
|
|
instanceOffsets: [offsetsBuffer, {divisor: 1}],
|
|
instanceColors: [colorsBuffer, {divisor: 1}],
|
|
instancePickingColors: [pickingColorsBuffer, {divisor: 1}]
|
|
}
|
|
})
|
|
);
|
|
}
|
|
}
|
|
|
|
class AppAnimationLoop extends AnimationLoop {
|
|
constructor() {
|
|
super({createFramebuffer: true, debug: true});
|
|
}
|
|
|
|
static getInfo() {
|
|
return INFO_HTML;
|
|
}
|
|
|
|
onInitialize({gl, _animationLoop}) {
|
|
setParameters(gl, {
|
|
clearColor: [0, 0, 0, 1],
|
|
clearDepth: 1,
|
|
depthTest: true,
|
|
depthFunc: gl.LEQUAL
|
|
});
|
|
|
|
this.attachTimeline(new Timeline());
|
|
this.timeline.play();
|
|
|
|
const timelineChannels = {
|
|
timeChannel: this.timeline.addChannel({
|
|
rate: 0.01
|
|
}),
|
|
|
|
eyeXChannel: this.timeline.addChannel({
|
|
rate: 0.0003
|
|
}),
|
|
|
|
eyeYChannel: this.timeline.addChannel({
|
|
rate: 0.0004
|
|
}),
|
|
|
|
eyeZChannel: this.timeline.addChannel({
|
|
rate: 0.0002
|
|
})
|
|
};
|
|
|
|
this.cube = new InstancedCube(gl);
|
|
|
|
const modelMatrix = mat4.create();
|
|
const viewMatix = mat4.create();
|
|
const projMatrix = mat4.create();
|
|
|
|
return {timelineChannels, modelMatrix, viewMatix, projMatrix};
|
|
}
|
|
|
|
onRender(animationProps) {
|
|
const {gl, aspect, tick, timelineChannels, modelMatrix, viewMatix, projMatrix} = animationProps;
|
|
const {framebuffer, _mousePosition} = animationProps;
|
|
const {timeChannel, eyeXChannel, eyeYChannel, eyeZChannel} = timelineChannels;
|
|
|
|
if (_mousePosition) {
|
|
// use the center pixel location in device pixel range
|
|
const devicePixels = cssToDevicePixels(gl, _mousePosition);
|
|
const deviceX = devicePixels.x + Math.floor(devicePixels.width / 2);
|
|
const deviceY = devicePixels.y + Math.floor(devicePixels.height / 2);
|
|
|
|
pickInstance(gl, deviceX, deviceY, this.cube, framebuffer);
|
|
}
|
|
|
|
mat4.identity(modelMatrix);
|
|
mat4.rotateX(modelMatrix, modelMatrix, tick * 0.01);
|
|
mat4.rotateY(modelMatrix, modelMatrix, tick * 0.013);
|
|
|
|
// Draw the cubes
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
this.cube.setUniforms({
|
|
uTime: this.timeline.getTime(timeChannel),
|
|
uProjection: mat4.perspective(projMatrix, Math.PI / 3, aspect, 1, 2048.0),
|
|
uView: mat4.lookAt(viewMatix,
|
|
[
|
|
(Math.cos(this.timeline.getTime(eyeXChannel)) * SIDE) / 2,
|
|
(Math.sin(this.timeline.getTime(eyeYChannel)) * SIDE) / 2,
|
|
((Math.sin(this.timeline.getTime(eyeZChannel)) + 1) * SIDE) / 4 + 32
|
|
],
|
|
[0, 0, 0],
|
|
[0, 1, 0]
|
|
),
|
|
uModel: modelMatrix
|
|
});
|
|
this.cube.draw();
|
|
}
|
|
|
|
onFinalize({gl}) {
|
|
this.cube.delete();
|
|
}
|
|
}
|
|
|
|
function pickInstance(gl, pickX, pickY, model, framebuffer) {
|
|
framebuffer.clear({color: true, depth: true});
|
|
// Render picking colors
|
|
/* eslint-disable camelcase */
|
|
model.setUniforms({picking_uActive: 1});
|
|
model.draw({framebuffer});
|
|
model.setUniforms({picking_uActive: 0});
|
|
|
|
const color = readPixelsToArray(framebuffer, {
|
|
sourceX: pickX,
|
|
sourceY: pickY,
|
|
sourceWidth: 1,
|
|
sourceHeight: 1,
|
|
sourceFormat: gl.RGBA,
|
|
sourceType: gl.UNSIGNED_BYTE
|
|
});
|
|
|
|
if (color[0] + color[1] + color[2] > 0) {
|
|
model.updateModuleSettings({
|
|
pickingSelectedColor: color
|
|
});
|
|
} else {
|
|
model.updateModuleSettings({
|
|
pickingSelectedColor: null
|
|
});
|
|
}
|
|
}
|
|
|
|
const animationLoop = new AppAnimationLoop();
|
|
animationLoop.start();
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|