<!doctype html>
<head>
<title>instancing1</title>
<script src="minMatrix.js"></script>
</head>
<body>
<canvas id="canvas" width="640" height="480"></canvas>
<div id="msg"></div>
<script id="vs" type="text/x-vertex">
// 頂点シェイダー
attribute vec3 position;
attribute vec4 color;
attribute vec3 instancePosition;
attribute vec4 instanceColor;
uniform mat4 mvpMatrix;
varying vec4 vColor;
void main(void) {
vColor = color * instanceColor;
gl_Position = mvpMatrix * vec4(position + instancePosition, 1.0);
}
</script>
<script id="fs" type="text/x-fragment">
// 断片シェイダー
precision mediump float;
varying vec4 vColor;
void main(void) {
gl_FragColor = vColor;
}
</script>
<script>
var gl; // webgl context
var ext;
var mat = new matIV();
var pMatrix = mat.identity(mat.create()); // perspective
var loc = {};
var indexCount;
var instanceCount = 0;
var lat = 0; // 緯度
var lon = 90; // 経度
var dist = 3; // 距離
onload = function() {
gl = canvas.getContext("experimental-webgl");
ext = gl.getExtension("ANGLE_instanced_arrays");
var vShader = createShader("vs");
var fShader = createShader("fs");
var program = createProgram(vShader, fShader);
gl.useProgram(program);
loc.position = gl.getAttribLocation(program, "position");
loc.color = gl.getAttribLocation(program, "color");
loc.instancePosition = gl.getAttribLocation(program, "instancePosition");
loc.instanceColor = gl.getAttribLocation(program, "instanceColor");
loc.mvpMatrix = gl.getUniformLocation(program, "mvpMatrix");
// mesh
var mesh = createCube();
var vbo = {};
vbo.position = createVbo(mesh.positions);
vbo.color = createVbo(mesh.colors);
var ibo = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array(mesh.indices), gl.STATIC_DRAW);
indexCount = mesh.indices.length;
// instance
var instance = createInstance();
vbo.instancePosition = createVbo(instance.positions);
vbo.instanceColor = createVbo(instance.colors);
gl.enable(gl.CULL_FACE);
gl.frontFace(gl.CW);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
setAttribute(loc.position, vbo.position, 3);
setAttribute(loc.color, vbo.color, 4);
setAttribute(loc.instancePosition, vbo.instancePosition, 3);
ext.vertexAttribDivisorANGLE(loc.instancePosition, 1);
setAttribute(loc.instanceColor, vbo.instanceColor, 4);
ext.vertexAttribDivisorANGLE(loc.instanceColor, 1);
mat.perspective(45, canvas.width / canvas.height, 0.1, 40, pMatrix);
draw();
// イベント
var px, py;
var drag = false;
canvas.onmousedown = function(e) {
drag = true;
};
canvas.onmouseup = function(e) {
drag = false;
};
canvas.onmousemove = function(e) {
if (drag) {
lat = Math.min(Math.max(lat + (e.clientY - py), -90), 90);
lon += (e.clientX - px);
while (lon < 0) lon += 360;
lon %= 360;
}
px = e.clientX;
py = e.clientY;
};
canvas.onmousewheel = function(e) {
dist = Math.max(dist - 0.2 * e.wheelDelta / 120, 0);
};
};
function draw() {
msg.innerHTML = "lat:" + lat + " lon:" + lon + " dist:" + dist.toFixed(1);
// clear
gl.clearColor(0x64 / 0xff, 0x95 / 0xff, 0xed / 0xff, 1.0);
gl.clearDepth(1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// camera
var rad = lat * Math.PI / 180;
var y = Math.sin(rad) * dist;
var r = Math.cos(rad) * dist;
var rad = lon * Math.PI / 180;
var x = Math.cos(rad) * r;
var z = Math.sin(rad) * r;
var vMatrix = mat.create();
var vpMatrix = mat.create();
mat.lookAt([x, y, z], [0, 0, 0], [0, 1, 0], vMatrix);
mat.multiply(pMatrix, vMatrix, vpMatrix);
// model
var mMatrix = mat.identity(mat.create());
var mvpMatrix = mat.create();
mat.multiply(vpMatrix, mMatrix, mvpMatrix);
gl.uniformMatrix4fv(loc.mvpMatrix, false, mvpMatrix);
// gl.drawElements(gl.TRIANGLE_STRIP, indexCount, gl.UNSIGNED_SHORT, 0);
ext.drawElementsInstancedANGLE(
gl.TRIANGLE_STRIP, indexCount, gl.UNSIGNED_SHORT, 0, instanceCount);
gl.flush();
setTimeout(draw, 1000 / 30);
}
function createShader(id) {
var element = document.getElementById(id);
if (! element) return;
const types = {
"text/x-vertex": gl.VERTEX_SHADER,
"text/x-fragment": gl.FRAGMENT_SHADER,
};
var shader = gl.createShader(types[element.type]);
gl.shaderSource(shader, element.text);
gl.compileShader(shader);
if (! gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return;
}
return shader;
}
function createProgram(vs, fs) {
var program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
if (! gl.getProgramParameter(program, gl.LINK_STATUS)) {
alert(gl.getProgramInfoLog(program));
return;
}
return program;
}
function createVbo(data) {
var vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
return vbo;
}
function setAttribute(location, vbo, stride) {
gl.enableVertexAttribArray(location);
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.vertexAttribPointer(location, stride, gl.FLOAT, false, 0, 0);
}
function createCube() {
var x = 0.5;
var positions = [
-x, x, -x,
x, x, -x,
-x, x, x,
x, x, x,
-x, -x, x,
x, -x, x,
-x, -x, -x,
x, -x, -x,
];
var colors = [
0.3, 0.3, 0.3, 1,
0.7, 0.7, 0.7, 1,
0.7, 0.7, 0.7, 1,
1.0, 1.0, 1.0, 1,
0.3, 0.3, 0.3, 1,
0.7, 0.7, 0.7, 1,
0.0, 0.0, 0.0, 1,
0.3, 0.3, 0.3, 1,
];
var indices = [
4, 6, 2, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 1, 7, 3, 5,
];
return {"positions":positions, "colors":colors, "indices":indices};
}
function createInstance() {
var positions = [];
var colors = [];
for (var z = 0; z < 10; z++) {
for (var x = 0; x < 10; x++) {
positions.push(2 * x, 0, -2 * z);
colors.push(x / 10, 0, z / 10, 1);
instanceCount++;
}
}
return {"positions":positions, "colors":colors};
}
</script>
</body>