mirror of
https://github.com/greggman/twgl.js.git
synced 2026-01-18 14:55:17 +00:00
454 lines
14 KiB
HTML
454 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf8">
|
|
<!--
|
|
|
|
@license twgl.js Copyright (c) 2015, Gregg Tavares All Rights Reserved.
|
|
Available via the MIT license.
|
|
see: http://github.com/greggman/twgl.js for details
|
|
|
|
-->
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
|
<meta property="og:title" content="TWGL.js - fov-checker" />
|
|
<meta property="og:type" content="website" />
|
|
<meta property="og:image" content="http://twgljs.org/examples/screenshots/fov-checker.png" />
|
|
<meta property="og:description" content="TWGL.js - fov-checker" />
|
|
<meta property="og:url" content="http://twgljs.org" />
|
|
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="twitter:site" content="@greggman">
|
|
<meta name="twitter:creator" content="@greggman">
|
|
<meta name="twitter:domain" content="twgljs.org">
|
|
<meta name="twitter:title" content="TWGL.js - fov-checker">
|
|
<meta name="twitter:url" content="http://twgljs.org/examples/fov-checker.html">
|
|
<meta name="twitter:description" content="TWGL.js - fov-checker">
|
|
<meta name="twitter:image:src" content="http://twgljs.org/examples/screenshots/fov-checker.png">
|
|
|
|
<link href="/resources/images/twgljs-icon.png" rel="shortcut icon" type="image/png">
|
|
|
|
<title>twgl.js - fov checker</title>
|
|
<style>
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
html, body {
|
|
margin: 0;
|
|
font-family: monospace;
|
|
height: 100%;
|
|
}
|
|
canvas {
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
#b {
|
|
position: absolute;
|
|
top: 10px;
|
|
width: 100%;
|
|
text-align: center;
|
|
z-index: 2;
|
|
color: white;
|
|
}
|
|
a:visited, a:link, a:active {
|
|
color: white;
|
|
}
|
|
#u {
|
|
color: white;
|
|
position: absolute;
|
|
padding: 1em;
|
|
bottom: 20px;
|
|
left: 20px;
|
|
background-color: rgba(192, 192, 192, 0.5);
|
|
width: 30%;
|
|
min-width: 300px;
|
|
text-align: center;
|
|
}
|
|
#u input[type=range] {
|
|
width: 90%;
|
|
}
|
|
#u tr>td:first-child {
|
|
text-align: right;
|
|
}
|
|
#u tr>td:first-child+td {
|
|
text-align: right;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<canvas id="c"></canvas>
|
|
<div id="b"><a href="http://twgljs.org">twgl.js - fov checker</a></div>
|
|
<div id="u">
|
|
<input id="fov" type="range" min="1" max="179"></input>
|
|
<table>
|
|
<tr><td> fov y: </td><td id="fovy"></td></tr>
|
|
<tr><td> fov x: </td><td id="fovx"></td></tr>
|
|
<tr><td>bad fov x: </td><td id="badfovx"></td></tr>
|
|
<tr><td> width: </td><td id="width"></td></tr>
|
|
<tr><td> height: </td><td id="height"></td></tr>
|
|
<tr><td> aspect: </td><td id="aspect"></td></tr>
|
|
</table>
|
|
</div>
|
|
</body>
|
|
<script id="vs" type="notjs">
|
|
uniform mat4 u_viewProjection;
|
|
uniform mat4 u_world;
|
|
|
|
attribute vec4 a_position;
|
|
|
|
void main() {
|
|
gl_Position = u_viewProjection * u_world * a_position;
|
|
}
|
|
</script>
|
|
<script id="fs" type="notjs">
|
|
precision mediump float;
|
|
|
|
uniform vec4 u_color;
|
|
|
|
void main() {
|
|
gl_FragColor = u_color;
|
|
}
|
|
</script>
|
|
<script src="../3rdparty/chroma.min.js"></script>
|
|
<script type="module">
|
|
import * as twgl from '../dist/7.x/twgl-full.module.js';
|
|
const m4 = twgl.m4;
|
|
twgl.setDefaults({attribPrefix: "a_"});
|
|
const gl = document.querySelector("#c").getContext("webgl");
|
|
const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
|
|
|
|
function createSphere(
|
|
gl,
|
|
radius,
|
|
subdivisionsAxis,
|
|
subdivisionsHeight,
|
|
hConnect,
|
|
vConnect,
|
|
startLatitudeInRadians,
|
|
endLatitudeInRadians,
|
|
startLongitudeInRadians,
|
|
endLongitudeInRadians) {
|
|
|
|
hConnect = hConnect === false ? false : true;
|
|
vConnect = vConnect === false ? false : true;
|
|
|
|
startLatitudeInRadians = startLatitudeInRadians || 0;
|
|
endLatitudeInRadians = endLatitudeInRadians || Math.PI;
|
|
startLongitudeInRadians = startLongitudeInRadians || 0;
|
|
endLongitudeInRadians = endLongitudeInRadians || Math.PI * 2;
|
|
|
|
const latRange = endLatitudeInRadians - startLatitudeInRadians;
|
|
const longRange = endLongitudeInRadians - startLongitudeInRadians;
|
|
|
|
// We are going to generate our sphere by iterating through its
|
|
// spherical coordinates and generating 2 lines for each quad on a
|
|
// ring of the sphere.
|
|
const numVertices = (subdivisionsAxis + 1) * (subdivisionsHeight + 1);
|
|
const positions = twgl.primitives.createAugmentedTypedArray(3, numVertices);
|
|
const normals = twgl.primitives.createAugmentedTypedArray(3, numVertices);
|
|
const texCoords = twgl.primitives.createAugmentedTypedArray(2 , numVertices);
|
|
|
|
// Generate the individual vertices in our vertex buffer.
|
|
for (let y = 0; y <= subdivisionsHeight; y++) {
|
|
for (let x = 0; x <= subdivisionsAxis; x++) {
|
|
// Generate a vertex based on its spherical coordinates
|
|
const u = x / subdivisionsAxis;
|
|
const v = y / subdivisionsHeight;
|
|
const theta = startLongitudeInRadians + longRange * u;
|
|
const phi = startLatitudeInRadians + latRange * v;
|
|
const sinTheta = Math.sin(theta);
|
|
const cosTheta = Math.cos(theta);
|
|
const sinPhi = Math.sin(phi);
|
|
const cosPhi = Math.cos(phi);
|
|
const ux = cosTheta * sinPhi;
|
|
const uy = cosPhi;
|
|
const uz = sinTheta * sinPhi;
|
|
positions.push(radius * ux, radius * uy, radius * uz);
|
|
normals.push(ux, uy, uz);
|
|
texCoords.push(1 - u, v);
|
|
}
|
|
}
|
|
|
|
const numVertsAround = subdivisionsAxis + 1;
|
|
const linesPerQuad = (hConnect ? 1 : 0) + (vConnect ? 1 : 0);
|
|
const indices = twgl.primitives.createAugmentedTypedArray(2, subdivisionsAxis * subdivisionsHeight * linesPerQuad, Uint16Array);
|
|
for (let x = 0; x < subdivisionsAxis; x++) { // eslint-disable-line
|
|
for (let y = 0; y < subdivisionsHeight; y++) { // eslint-disable-line
|
|
// Make hline of quad.
|
|
if (hConnect) {
|
|
indices.push(
|
|
(y + 0) * numVertsAround + x,
|
|
(y + 0) * numVertsAround + x + 1);
|
|
}
|
|
|
|
// Make vline of quad.
|
|
if (vConnect) {
|
|
indices.push(
|
|
(y + 1) * numVertsAround + x,
|
|
(y + 0) * numVertsAround + x);
|
|
}
|
|
}
|
|
}
|
|
|
|
return twgl.createBufferInfoFromArrays(gl, {
|
|
position: positions,
|
|
normal: normals,
|
|
texcoord: texCoords,
|
|
indices: indices,
|
|
});
|
|
}
|
|
|
|
function deg2Rad(d) {
|
|
return d * Math.PI / 180;
|
|
}
|
|
|
|
function rad2Deg(r) {
|
|
return r * 180 / Math.PI;
|
|
}
|
|
|
|
const radius = 10;
|
|
const a = Math.PI * 1.5;
|
|
const b = Math.PI * 0.5;
|
|
const c = 0.01;
|
|
const vMarksSphere = createSphere(gl, radius, 2, 180, true, false, 0, Math.PI, a - c, a + c);
|
|
const hMarksSphere = createSphere(gl, radius, 360, 2, false, true, b - c, b + c, 0, Math.PI * 2);
|
|
const highDetailSphere = createSphere(gl, radius, 36 * 2, 18 * 2);
|
|
const detailSphere = createSphere(gl, radius, 36, 18);
|
|
const v3rdsSphere = createSphere(gl, radius, 12, 36, false, true);
|
|
const h3rdsSphere = createSphere(gl, radius, 36, 6, true, false);
|
|
const vQuadSphere = createSphere(gl, radius, 4, 36, false, true);
|
|
const hQuadSphere = createSphere(gl, radius, 36, 2, true, false);
|
|
|
|
/**
|
|
* 0---1
|
|
* | |
|
|
* 2---3
|
|
* | |
|
|
* 4---5
|
|
*/
|
|
const numberPositions = [
|
|
-1, 1, 1, 1,
|
|
-1, 0, 1, 0,
|
|
-1, -1, 1, -1,
|
|
];
|
|
|
|
const numberIndices = [
|
|
[ 0, 1, 1, 5, 5, 4, 4, 0 ], // 0
|
|
[ 1, 5 ], // 1
|
|
[ 0, 1, 1, 3, 3, 2, 2, 4, 4, 5 ], // 2
|
|
[ 0, 1, 1, 5, 5, 4, 2, 3 ], // 3
|
|
[ 0, 2, 2, 3, 1, 5 ], // 4
|
|
[ 1, 0, 0, 2, 2, 3, 3, 5, 5, 4 ], // 5
|
|
[ 1, 0, 0, 4, 4, 5, 5, 3, 3, 2 ], // 6
|
|
[ 0, 1, 1, 5 ], // 7
|
|
[ 0, 1, 1, 5, 5, 4, 4, 0, 2, 3 ], // 8
|
|
[ 3, 2, 2, 0, 0, 1, 1, 5 ], // 9
|
|
];
|
|
|
|
let total = 0;
|
|
const numberInfos = numberIndices.map(function(indices) {
|
|
const offset = total;
|
|
const count = indices.length;
|
|
total += count;
|
|
return {
|
|
offset: offset * 2,
|
|
count: count,
|
|
};
|
|
});
|
|
|
|
const numberBufferInfo = twgl.createBufferInfoFromArrays(gl, {
|
|
position: { numComponents: 2, data: numberPositions },
|
|
indices: { numComponents: 2, data: Array.prototype.concat.apply([], numberIndices) },
|
|
});
|
|
|
|
let fieldOfViewY = deg2Rad(30);
|
|
const viewProjection = m4.identity();
|
|
|
|
const drawObjects = [
|
|
{ programInfo: programInfo,
|
|
bufferInfo: vMarksSphere,
|
|
type: gl.LINES,
|
|
uniforms: {
|
|
u_world: m4.identity(),
|
|
u_viewProjection: viewProjection,
|
|
u_color: [0.3, 0.3, 0.3, 1],
|
|
},
|
|
},
|
|
{ programInfo: programInfo,
|
|
bufferInfo: hMarksSphere,
|
|
type: gl.LINES,
|
|
uniforms: {
|
|
u_world: m4.identity(),
|
|
u_viewProjection: viewProjection,
|
|
u_color: [0.3, 0.3, 0.3, 1],
|
|
},
|
|
},
|
|
{ programInfo: programInfo,
|
|
bufferInfo: highDetailSphere,
|
|
type: gl.LINES,
|
|
uniforms: {
|
|
u_world: m4.identity(),
|
|
u_viewProjection: viewProjection,
|
|
u_color: [0.3, 0.3, 0.3, 1],
|
|
},
|
|
},
|
|
{ programInfo: programInfo,
|
|
bufferInfo: detailSphere,
|
|
type: gl.LINES,
|
|
uniforms: {
|
|
u_world: m4.identity(),
|
|
u_viewProjection: viewProjection,
|
|
u_color: [0, 0, 1, 1],
|
|
},
|
|
},
|
|
{ programInfo: programInfo,
|
|
bufferInfo: h3rdsSphere,
|
|
type: gl.LINES,
|
|
uniforms: {
|
|
u_world: m4.identity(),
|
|
u_viewProjection: viewProjection,
|
|
u_color: [0, 1, 0, 1],
|
|
},
|
|
},
|
|
{ programInfo: programInfo,
|
|
bufferInfo: v3rdsSphere,
|
|
type: gl.LINES,
|
|
uniforms: {
|
|
u_world: m4.identity(),
|
|
u_viewProjection: viewProjection,
|
|
u_color: [0, 1, 0, 1],
|
|
},
|
|
},
|
|
{ programInfo: programInfo,
|
|
bufferInfo: hQuadSphere,
|
|
type: gl.LINES,
|
|
uniforms: {
|
|
u_world: m4.identity(),
|
|
u_viewProjection: viewProjection,
|
|
u_color: [1, 0, 0, 1],
|
|
},
|
|
},
|
|
{ programInfo: programInfo,
|
|
bufferInfo: vQuadSphere,
|
|
type: gl.LINES,
|
|
uniforms: {
|
|
u_world: m4.identity(),
|
|
u_viewProjection: viewProjection,
|
|
u_color: [1, 0, 0, 1],
|
|
},
|
|
},
|
|
];
|
|
|
|
function addNumber(value, world) {
|
|
const str = value.toString();
|
|
const length = str.length;
|
|
const letterSize = 2.0;
|
|
const padding = 0.6;
|
|
const scale = [0.2, 0.2, 1];
|
|
const spacing = letterSize + padding;
|
|
const xOffset = (length - 1) * (spacing) * -0.5;
|
|
Array.prototype.forEach.call(str, function(digit, ndx) {
|
|
const mat = m4.copy(world);
|
|
m4.scale(mat, scale, mat);
|
|
m4.translate(mat, [xOffset + ndx * spacing, 0, 0], mat);
|
|
const digitNdx = parseInt(digit);
|
|
const numberInfo = numberInfos[digitNdx];
|
|
drawObjects.push({
|
|
programInfo: programInfo,
|
|
bufferInfo: numberBufferInfo,
|
|
type: gl.LINES,
|
|
offset: numberInfo.offset,
|
|
count: numberInfo.count,
|
|
uniforms: {
|
|
u_world: mat,
|
|
u_viewProjection: viewProjection,
|
|
u_color: [1, 1, 1, 1],
|
|
},
|
|
});
|
|
});
|
|
}
|
|
|
|
// Add numbers around sphere
|
|
for (let ii = -180; ii < 180; ii += 10) {
|
|
let world = m4.identity();
|
|
m4.rotateY(world, deg2Rad(ii), world);
|
|
m4.translate(world, [0, 0, -radius], world);
|
|
addNumber(Math.abs(ii), world);
|
|
|
|
world = m4.identity();
|
|
m4.rotateX(world, deg2Rad(ii), world);
|
|
m4.translate(world, [0, 0, -radius], world);
|
|
addNumber(Math.abs(ii), world);
|
|
}
|
|
|
|
function render() {
|
|
twgl.resizeCanvasToDisplaySize(gl.canvas);
|
|
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
|
|
|
gl.clearColor(0, 0, 0, 1);
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
|
|
gl.lineWidth(2);
|
|
|
|
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
|
|
const zNear = 0.1;
|
|
const zFar = 50;
|
|
m4.perspective(fieldOfViewY, aspect, zNear, zFar, viewProjection);
|
|
//m4.translate(matrix, [0, 0, -40], matrix);
|
|
|
|
twgl.drawObjectList(gl, drawObjects);
|
|
|
|
}
|
|
|
|
function createTextNodeInElement(id) {
|
|
const elem = document.getElementById(id);
|
|
const node = document.createTextNode("");
|
|
elem.appendChild(node);
|
|
return node;
|
|
}
|
|
|
|
function makeNodes(ids) {
|
|
const nodes = {};
|
|
ids.forEach(function(id) {
|
|
nodes[id] = createTextNodeInElement(id);
|
|
});
|
|
return nodes;
|
|
}
|
|
|
|
const rangeElem = document.getElementById("fov");
|
|
const nodes = makeNodes(["fovy", "fovx", "width", "height", "aspect", "badfovx"]);
|
|
rangeElem.value = 30;
|
|
|
|
function updateNodes() {
|
|
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
|
|
const fieldOfViewX = 2 * Math.atan(Math.tan(fieldOfViewY * 0.5) * aspect);
|
|
const badFieldOfViewX = fieldOfViewY * aspect;
|
|
|
|
nodes.fovy.nodeValue = rad2Deg(fieldOfViewY).toFixed(2);
|
|
nodes.fovx.nodeValue = rad2Deg(fieldOfViewX).toFixed(2);
|
|
nodes.badfovx.nodeValue = rad2Deg(badFieldOfViewX).toFixed(2);
|
|
nodes.width.nodeValue = gl.canvas.clientWidth;
|
|
nodes.height.nodeValue = gl.canvas.clientHeight;
|
|
nodes.aspect.nodeValue = aspect.toFixed(3);
|
|
}
|
|
updateNodes();
|
|
|
|
function updateFOV() {
|
|
fieldOfViewY = deg2Rad(rangeElem.value);
|
|
updateNodes();
|
|
render();
|
|
}
|
|
|
|
rangeElem.addEventListener('input', updateFOV, false);
|
|
window.addEventListener('resize', updateFOV, false);
|
|
|
|
render();
|
|
|
|
</script>
|
|
</html>
|
|
|
|
|
|
|
|
|
|
|