luma.gl/examples/script/index.html
2019-11-27 19:50:02 -05:00

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>