開発環境 メモ帳
実行環境 Microsoft Edge



sphere3.html
<!doctype html>
<head>
<title>sphere3</title>
<script src="minMatrix.js"></script>
</head>
 
<body>
<canvas id="canvas" width="640" height="480"></canvas>
<div id="msg"></div>
<input type="checkbox" onclick="wireframe = 1 - wireframe; draw();">wireframe
<script id="vs" type="text/x-vertex">
 
// 頂点シェイダー
attribute vec3 position;
attribute vec2 textureCoord;
uniform mat4 mvpMatrix;
varying vec2 vTextureCoord;
 
void main(void) {
	vTextureCoord = textureCoord;
	gl_Position = mvpMatrix * vec4(position, 1.0);
}
 
</script>
<script id="fs" type="text/x-fragment">
 
// 断片シェイダー
precision mediump float;
 
uniform sampler2D texture;
varying vec2 vTextureCoord;
 
void main(void) {
	gl_FragColor = texture2D(texture, vTextureCoord);
//	gl_FragColor = vec4(vTextureCoord, 0.0, 1.0);
}
 
</script>
<script>
 
var gl;	// webgl context
var uniLocation = [];	// uniform
var m = new matIV();
var mMatrix = m.identity(m.create());	// model
var pMatrix = m.identity(m.create());	// perspective
 
var positions;
var textureCoords;
var indices;
var texture = null;
var wireframe = 0;
var draws = [];
 
var lat = 0;	// 緯度
var lon = 90;	// 経度
var dist = 3;	// 距離
 
onload = function() {
	gl = canvas.getContext("experimental-webgl");
 
	var vShader = createShader("vs");
	var fShader = createShader("fs");
	var program = createProgram(vShader, fShader);
 
	gl.activeTexture(gl.TEXTURE0);
	createTexture("earthmap1k.jpg");
 
	sphere(1, 32);
 
	setAttribute(program, "position", positions, 3);
	setAttribute(program, "textureCoord", textureCoords, 2);
 
	var obj = {};
	obj.buffer = createIbo(indices);
	obj.mode = gl.TRIANGLES;
	obj.count = indices.length;
	draws[0] = obj;
 
	// wireframe
	var indiceswf = [];
	for (var i = 0; i < indices.length; i += 3) {
		indiceswf.push(indices[i + 0], indices[i + 1]);
		indiceswf.push(indices[i + 1], indices[i + 2]);
		indiceswf.push(indices[i + 2], indices[i + 0]);
	}
	var obj = {};
	obj.buffer = createIbo(indiceswf);
	obj.mode = gl.LINES;
	obj.count = indiceswf.length;
	draws[1] = obj;
 
	gl.enable(gl.CULL_FACE);
	gl.frontFace(gl.CW);
	gl.enable(gl.DEPTH_TEST);
	gl.depthFunc(gl.LEQUAL);
 
	uniLocation[0] = gl.getUniformLocation(program, "mvpMatrix");
	uniLocation[1] = gl.getUniformLocation(program, "texture");
 
	m.perspective(45, canvas.width / canvas.height, 0.1, 10, 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;
			draw();
		}
		px = e.clientX;
		py = e.clientY;
	};
	canvas.onmousewheel = function(e) {
		dist = Math.max(dist - 0.1 * e.wheelDelta / 120, 0);
		draw();
	};
};
 
function draw() {
	msg.innerHTML = "lat:" + lat + " lon:" + lon + " dist:" + dist.toFixed(1);
 
	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 = m.identity(m.create());
	var mvpMatrix = m.identity(m.create());
	m.lookAt([x, y, z], [0, 0, 0], [0, 1, 0], vMatrix);
	m.multiply(pMatrix, vMatrix, mvpMatrix);
	m.multiply(mvpMatrix, mMatrix, mvpMatrix);
 
//	gl.clearColor(0x64 / 0xff, 0x95 / 0xff, 0xed / 0xff, 1.0);
	gl.clearColor(0.0, 0.0, 0.0, 1.0);
	gl.clearDepth(1);
	gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
 
	gl.bindTexture(gl.TEXTURE_2D, texture);
	gl.uniform1i(uniLocation[1], 0);
	gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
 
	var obj = draws[wireframe];
	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.buffer);
	gl.drawElements(obj.mode, obj.count, gl.UNSIGNED_SHORT, 0);
 
	gl.flush();
}
 
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;
	}
	gl.useProgram(program);
	return program;
}
 
function setAttribute(program, name, data, stride) {
	var vbo = gl.createBuffer();
	gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
 
	var location = gl.getAttribLocation(program, name);
	gl.enableVertexAttribArray(location);
	gl.vertexAttribPointer(location, stride, gl.FLOAT, false, 0, 0);
	gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
 
function createIbo(indices) {
	var ibo = gl.createBuffer();
	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
	gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array(indices), gl.STATIC_DRAW);
	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
	return ibo;
}
 
function createTexture(src) {
	var img1 = new Image();
	img1.onload = function() {
		var canvas = document.createElement("canvas");
		canvas.width = 1024;
		canvas.height = 512;
		var ctx = canvas.getContext("2d");
		ctx.drawImage(img1, 0, 0, canvas.width, canvas.height);
 
		// 赤道
		ctx.strokeStyle = "#ff0000";
		ctx.beginPath();
		ctx.moveTo(0, 256);
		ctx.lineTo(1024, 256);
		ctx.closePath();
		ctx.stroke();
 
		var img2 = new Image();
		img2.onload = function() {
			var tex = gl.createTexture();
			gl.bindTexture(gl.TEXTURE_2D, tex);
			gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img2);
			gl.generateMipmap(gl.TEXTURE_2D);
			gl.bindTexture(gl.TEXTURE_2D, null);
			texture = tex;
		};
		img2.src = canvas.toDataURL("image/png");
	};
	img1.src = src;
}
 
function sphere(radius, stacks) {
 
	// 頂点
	positions = [];
	textureCoords = [];
	positions.push(0, 1, 0);	// x, y, z
	textureCoords.push(0.5, 0);
	for (var stack = 1; stack < stacks; stack++) {
		var v = stack / stacks;
		var lat = Math.PI * v;
		var r = Math.sin(lat);
		var y = Math.cos(lat);
		var slices = 4 * Math.min(stack, stacks - stack);	// 放射頂点の数
		for (var slice = 0; slice <= slices; slice++) {
			var u = slice / slices;
			var lon = 2 * Math.PI * u;
			var z = Math.sin(lon) * r;
			var x = Math.cos(lon) * r;
			positions.push(x, y, z);
			textureCoords.push(1.0 - u, v);
		}
	}
	positions.push(0, -1, 0);
	textureCoords.push(0.5, 1);
 
	// 索引
	indices = [];
	var prevHead = 0;	// 前の先頭頂点番号
	var prevVert = 1;	// 前の頂点数
	for (var stack = 0; stack < stacks / 2; stack++) {
		var currHead = prevHead + prevVert;	// 現在の先頭頂点番号
		var currVert = 4 * (stack + 1) + 1;	// 現在の頂点数
		for (var quad = 0; quad < 4; quad++) {	// 4象限
			var prevQuad = quad * stack;		// 前の象限オフセット
			var currQuad = quad * (stack + 1);	// 現在の象限オフセット
			for (var i = 0; ; i++) {
				var prev = prevHead + prevQuad + i;
				var curr = currHead + currQuad + i;
				indices.push(prev, curr, curr + 1);
				if (stack <= i) break;
				indices.push(prev, curr + 1, prev + 1);
			}
		}
		prevHead = currHead;
		prevVert = currVert;
	}
	for (var stack = stacks / 2 - 1; 0 <= stack; stack--) {
		var currHead = prevHead + prevVert;
		var currVert = 4 * stack + 1;
		for (var quad = 0; quad < 4; quad++) {
			var prevQuad = quad * (stack + 1);
			var currQuad = quad * stack;
			for (var i = 0; ; i++) {
				var prev = prevHead + prevQuad + i;
				var curr = currHead + currQuad + i;
				indices.push(curr, prev + 1, prev);
				if (stack <= i) break;
				indices.push(curr, curr + 1, prev + 1);
			}
		}
		prevHead = currHead;
		prevVert = currVert;
	}
}
 
</script>
</body>
 
最終更新:2016年02月16日 19:22