initial commit
This commit is contained in:
commit
fd1c493e91
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.vscode
|
15
index.html
Normal file
15
index.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LauchSpace</title>
|
||||
</head>
|
||||
<body>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="orbitals.html">Orbitals</a>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
71
orbitals.html
Normal file
71
orbitals.html
Normal file
|
@ -0,0 +1,71 @@
|
|||
<!DOCTYPE html5>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Orbitals</title>
|
||||
|
||||
<script src="orbitals/legendre.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"
|
||||
integrity="sha512-zhHQR0/H5SEBL3Wn6yYSaTTZej12z0hVZKOv3TwCUXT1z5qeqGcXJLLrbERYRScEDDpYIJhPC1fk31gqR783iQ=="
|
||||
crossorigin="anonymous" defer>
|
||||
</script>
|
||||
<script src="orbitals/rendering.js" defer></script>
|
||||
|
||||
<style>
|
||||
#screen
|
||||
{
|
||||
border: 1px solid red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="float: left; margin-right: 50px;">
|
||||
<canvas id="screen" width="500" height="500"></canvas>
|
||||
</div>
|
||||
|
||||
<h1>What is this?</h1>
|
||||
|
||||
<p>
|
||||
This little WebGL script visualizes the <a href="https://www.wikiwand.com/en/Spherical_harmonics">Spherical Harmonics</a>.
|
||||
A quick rundown about what that means:
|
||||
</p>
|
||||
<p>
|
||||
The <i>Spherical Harmonics</i> are results of a rather complex (pun not intended) function, commonly denoted with the capital letter Y.
|
||||
This function depends on two parameters: <i>l</i> and <i>m</i>. Y is defined for all <b>integers</b> <i>l</i> and <i>m</i> such that 0 ≤ <i>m</i> ≤ <i>l</i>.
|
||||
The function takes in two variables: θ and φ. The function essentially takes in a sphere and deforms it in a specific way.
|
||||
</p>
|
||||
<p>
|
||||
This tool plots this function. Since the results of the function are complex, it isn't really easy to plot the function in all its glory.
|
||||
(The function takes in two variables and spits out another two. We'd need 4D screens to see it in all its glory).
|
||||
So this widget just plots the radius of the complex number. Think of complex numbers as points in 2D space. I plot the distance
|
||||
of that point from the origin.
|
||||
</p>
|
||||
<br>
|
||||
<br>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Point distance</td>
|
||||
<td>--</td>
|
||||
<td><input type="range" min="0.01" max="0.1" step="0.005" value="0.05" id="stepSize" /></td>
|
||||
<td id="lstepSize"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>l</td>
|
||||
<td>--</td>
|
||||
<td><input type="range" min="0" max="10" step="1" value="2" id="l" /></td>
|
||||
<td id="ll"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>m</td>
|
||||
<td>--</td>
|
||||
<td><input type="range" min="-10" max="10" step="1" value="0" id="m" /></td>
|
||||
<td id="lm"></td>
|
||||
</tr>
|
||||
|
||||
<tr><td><button onclick="createModel()">Create</button></td></tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
38
orbitals/legendre.js
Normal file
38
orbitals/legendre.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
function fact(n)
|
||||
{
|
||||
if (n <= 1)
|
||||
return 1
|
||||
|
||||
for (var i = n - 1; i >= 1; i--)
|
||||
n *= i;
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
function assoc_legendre(l, m, x)
|
||||
{
|
||||
l = Math.floor(l);
|
||||
m = Math.floor(m);
|
||||
|
||||
if (x > 1 || x < -1 || l < 0 || m > l)
|
||||
return 0;
|
||||
|
||||
if (m === l)
|
||||
return (Math.pow(-1, m) * fact(2*m) / (Math.pow(2, m) * fact(m)) * Math.pow(1 - x*x, m/2))
|
||||
|
||||
return (x * (2*l - 1) * assoc_legendre(l-1, m, x) - (l + m - 1) * assoc_legendre(l-2, m, x)) / (l - m)
|
||||
}
|
||||
|
||||
function N(l, m)
|
||||
{
|
||||
return Math.sqrt((2*l + 1)/2 * fact(l-m) / fact(l+m));
|
||||
}
|
||||
|
||||
function Y(l, m, theta, phi)
|
||||
{
|
||||
var a = 1 / Math.sqrt(2 * Math.PI) * N(l, Math.abs(m)) * assoc_legendre(l, Math.abs(m), Math.cos(theta))
|
||||
if(m >= 0)
|
||||
return a * Math.cos(m * phi)
|
||||
else
|
||||
return a * Math.sin(Math.abs(m) * phi)
|
||||
}
|
149
orbitals/rendering.js
Normal file
149
orbitals/rendering.js
Normal file
|
@ -0,0 +1,149 @@
|
|||
function HSVtoRGB(h, s, v) {
|
||||
var r, g, b, i, f, p, q, t;
|
||||
if (arguments.length === 1) {
|
||||
s = h.s, v = h.v, h = h.h;
|
||||
}
|
||||
i = Math.floor(h * 6);
|
||||
f = h * 6 - i;
|
||||
p = v * (1 - s);
|
||||
q = v * (1 - f * s);
|
||||
t = v * (1 - (1 - f) * s);
|
||||
switch (i % 6) {
|
||||
case 0: r = v, g = t, b = p; break;
|
||||
case 1: r = q, g = v, b = p; break;
|
||||
case 2: r = p, g = v, b = t; break;
|
||||
case 3: r = p, g = q, b = v; break;
|
||||
case 4: r = t, g = p, b = v; break;
|
||||
case 5: r = v, g = p, b = q; break;
|
||||
}
|
||||
return {
|
||||
r: Math.round(r * 255),
|
||||
g: Math.round(g * 255),
|
||||
b: Math.round(b * 255)
|
||||
};
|
||||
}
|
||||
|
||||
var canvas = document.querySelector('#screen');
|
||||
canvas.height = screen.height - 200;
|
||||
canvas.width = canvas.height;
|
||||
console.log(window.height)
|
||||
var gl = canvas.getContext("webgl");
|
||||
|
||||
var VBO = gl.createBuffer();
|
||||
|
||||
var vertices = [];
|
||||
function createModel()
|
||||
{
|
||||
var stepSize = Number(document.getElementById("stepSize").value);
|
||||
var l = Number(document.getElementById("l").value);
|
||||
var m = Number(document.getElementById("m").value);
|
||||
|
||||
document.getElementById("lstepSize").innerHTML = stepSize;
|
||||
document.getElementById("ll").innerHTML = l;
|
||||
document.getElementById("lm").innerHTML = m;
|
||||
|
||||
vertices = [];
|
||||
for(var theta = 0; theta <= Math.PI; theta += stepSize)
|
||||
{
|
||||
for(var phi = -Math.PI; phi <= Math.PI; phi += stepSize)
|
||||
{
|
||||
var length = Math.abs(Y(l, m, theta, phi));
|
||||
|
||||
var x = length * Math.sin(theta) * Math.cos(phi);
|
||||
var y = length * Math.sin(theta) * Math.sin(phi);
|
||||
var z = length * Math.cos(theta);
|
||||
|
||||
vertices.push(x);
|
||||
vertices.push(y);
|
||||
vertices.push(z);
|
||||
}
|
||||
}
|
||||
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, VBO);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
||||
}
|
||||
|
||||
gl.clearColor(0, 0, 0, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
createModel();
|
||||
|
||||
var vertCode =
|
||||
'attribute vec3 coordinates;' +
|
||||
'uniform mat4 uModelMatrix;' +
|
||||
'uniform mat4 uModelViewMatrix;' +
|
||||
'uniform mat4 uProjectionMatrix;' +
|
||||
'void main(void) {' +
|
||||
' gl_Position = uProjectionMatrix * uModelViewMatrix * uModelMatrix * vec4(coordinates, 1.0);' +
|
||||
' gl_PointSize = 1.0;' +
|
||||
'}';
|
||||
|
||||
var vertShader = gl.createShader(gl.VERTEX_SHADER);
|
||||
gl.shaderSource(vertShader, vertCode);
|
||||
gl.compileShader(vertShader);
|
||||
|
||||
var fragCode =
|
||||
'void main(void) {' + 'gl_FragColor = vec4(0.2, 0.9, 0.2, 1.0);' + '}';
|
||||
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
|
||||
gl.shaderSource(fragShader, fragCode);
|
||||
gl.compileShader(fragShader);
|
||||
|
||||
var shaderProgram = gl.createProgram();
|
||||
gl.attachShader(shaderProgram, vertShader);
|
||||
gl.attachShader(shaderProgram, fragShader);
|
||||
gl.linkProgram(shaderProgram);
|
||||
|
||||
var modelMat = mat4.create()
|
||||
mat4.rotate(modelMat, modelMat, Math.PI / 2, [1.0, 0.0, 0]);
|
||||
|
||||
var viewMat = mat4.create();
|
||||
mat4.translate(viewMat, viewMat, [0.0, 0.0, -3.0]);
|
||||
|
||||
function drawScene()
|
||||
{
|
||||
gl.clearColor(0, 0, 0, 1);
|
||||
gl.clearDepth(1.0);
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.depthFunc(gl.LEQUAL)
|
||||
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
|
||||
const fov = 45 * Math.PI / 180;
|
||||
const aspect = canvas.width / canvas.height;
|
||||
const zNear = 0.1;
|
||||
const zFar = 100.0;
|
||||
|
||||
const projMat = mat4.create();
|
||||
mat4.perspective(projMat, fov, aspect, zNear, zFar);
|
||||
|
||||
mat4.rotate(viewMat, viewMat, 0.05, [0.0, 1.0, 0.0]);
|
||||
|
||||
gl.useProgram(shaderProgram);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, VBO);
|
||||
|
||||
gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram, "uModelMatrix"), false, modelMat);
|
||||
gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram, "uModelViewMatrix"), false, viewMat);
|
||||
gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram, "uProjectionMatrix"), false, projMat);
|
||||
|
||||
var coord = gl.getAttribLocation(shaderProgram, "coordinates");
|
||||
|
||||
gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
|
||||
gl.enableVertexAttribArray(coord);
|
||||
|
||||
gl.viewport(0,0,canvas.width,canvas.height);
|
||||
gl.drawArrays(gl.POINTS, 0, vertices.length / 3);
|
||||
}
|
||||
|
||||
var then = 0;
|
||||
|
||||
function render(now) {
|
||||
now *= 0.001; // convert to seconds
|
||||
const deltaTime = now - then;
|
||||
then = now;
|
||||
|
||||
drawScene();
|
||||
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
requestAnimationFrame(render);
|
Loading…
Reference in a new issue