first commit
This commit is contained in:
commit
42778a9d46
13 changed files with 1192 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/webGLTest.iml
|
||||
/.idea/
|
34
index.html
Normal file
34
index.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>WEBGL Test</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="c" width="1900" height="1000"></canvas>
|
||||
<div class="off-can closed">
|
||||
<group id="rotate">
|
||||
<group-label>Rotation</group-label>
|
||||
</group>
|
||||
<group id="translate">
|
||||
<group-label>Transform</group-label>
|
||||
</group>
|
||||
<group id="color">
|
||||
<group-label>Color</group-label>
|
||||
</group>
|
||||
<group id="world">
|
||||
<group-label>World</group-label>
|
||||
</group>
|
||||
<group id="draw">
|
||||
<group-label>Draw</group-label>
|
||||
</group>
|
||||
</div>
|
||||
<div class="settings-icon">☰</div>
|
||||
<script src="js/utils.js" defer></script>
|
||||
<script src="js/handler.js"></script>
|
||||
<script src="js/index.js"></script>
|
||||
<script src="js/sphere.js"></script>
|
||||
<script src="js/gui.js"></script>
|
||||
</body>
|
||||
</html>
|
208
js/gui.js
Normal file
208
js/gui.js
Normal file
|
@ -0,0 +1,208 @@
|
|||
function $(sel, s) {
|
||||
return $$(sel, s)[0];
|
||||
}
|
||||
|
||||
function $$(sel, s) {
|
||||
s = s || document;
|
||||
return s.querySelectorAll(sel);
|
||||
}
|
||||
|
||||
Node.prototype.addDelegatedEventListener = function (type, aim, cb) {
|
||||
this.addEventListener(type, (event) => {
|
||||
let target = event.target;
|
||||
if (target.matches(aim)) {
|
||||
cb(event, target);
|
||||
} else {
|
||||
let parent = target.closest(aim);
|
||||
if (parent) {
|
||||
cb(event, parent);
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
function create(name, content) {
|
||||
let d = document.createElement(name);
|
||||
if (content) {
|
||||
d.innerHTML = content;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
function append(to, array) {
|
||||
for (let item in array) {
|
||||
to.appendChild(array[item]);
|
||||
}
|
||||
}
|
||||
|
||||
function generateRotationSliders() {
|
||||
let group = $('#rotate');
|
||||
generateSlider(["X", "Y", "Z", "X-Inc", "Y-Inc", "Z-Inc"], 0, 360, 0, group, "rotate");
|
||||
generateCheckBox(["by Beat"], true, group, "rotate");
|
||||
}
|
||||
|
||||
function generateTranslateSliders() {
|
||||
let group = $('#translate');
|
||||
generateSlider(["X", "Y", "Z"], -1, 1, 1, group, "translate");
|
||||
}
|
||||
|
||||
function generateColorSliders() {
|
||||
let group = $('#color');
|
||||
generateSlider(["R", "G", "B"], 0, 255, 125, group, "color");
|
||||
generateCheckBox(["by Beat"], true, group, "color");
|
||||
}
|
||||
|
||||
function generateWorldSliders() {
|
||||
let group = $('#world');
|
||||
generateSlider(["LightPos-X", "LightPos-Y", "LightPos-Z"], -1, 1, 1, group, "light");
|
||||
generateSlider(["LightAngle"], 0, 360, 90, group, "light");
|
||||
}
|
||||
|
||||
function generateDrawSliders() {
|
||||
let group = $('#draw'),
|
||||
g = "drawMode"
|
||||
generateSlider(["DrawMode"], 0, 6, 0, group, g);
|
||||
generateSlider(["Form"], 0, 3, 0, group, g);
|
||||
generateSlider(["Radius"], 20, 1500, 500, group, g);
|
||||
generateSlider(["Total"], 0, 200, 50, group, g);
|
||||
generateSlider(["PointSize"], 1, 10, 2, group, g, .2);
|
||||
generateSlider(["Smoothing"], 0, 1, 0.8, group, g, .05);
|
||||
generateSlider(["Steps"], 512, analyser.fftSize / 2, 512, group, g, 256);
|
||||
generateCheckBox(["Dirty"], false, group, g)
|
||||
}
|
||||
|
||||
function setRotations(changedEl) {
|
||||
let group = $('#rotate');
|
||||
let items = ["#X", "#Y", "#Z"];
|
||||
for (let i in items) {
|
||||
let el = $(items[i], group);
|
||||
if (el === changedEl) {
|
||||
sphereObject.rotationRaw[i] = getValue(el)
|
||||
}
|
||||
}
|
||||
items = ["#X-Inc", "#Y-Inc", "#Z-Inc"];
|
||||
for (let i in items) {
|
||||
let el = $(items[i], group);
|
||||
if (el === changedEl) {
|
||||
sphereObject.rotateInc[i] = getValue(el) / 360;
|
||||
}
|
||||
}
|
||||
|
||||
sphereObject.rotateByBeat = $('#rotateby-Beat', group).checked;
|
||||
}
|
||||
|
||||
function setColors() {
|
||||
let group = $('#color');
|
||||
sphereObject.color.r = getValue($('#R', group)) / 255;
|
||||
sphereObject.color.g = getValue($('#G', group)) / 255;
|
||||
sphereObject.color.b = getValue($('#B', group)) / 255;
|
||||
|
||||
sphereObject.colorByBeat = $('#colorby-Beat', group).checked;
|
||||
}
|
||||
|
||||
function setWorld() {
|
||||
let group = $('#world');
|
||||
sphereObject.lightPos[0] = getValue($('#LightPos-X', group)) * 0.2;
|
||||
sphereObject.lightPos[1] = getValue($('#LightPos-Y', group)) * 0.2;
|
||||
sphereObject.lightPos[2] = getValue($('#LightPos-Z', group)) * 0.2;
|
||||
|
||||
lightAngle = getValue($('#LightAngle', group))
|
||||
}
|
||||
|
||||
function setDraw() {
|
||||
let group = $('#draw');
|
||||
sphereObject.drawMode = getValue($('#DrawMode', group));
|
||||
sphereObject.sphereMode = getValue($('#Form', group));
|
||||
sphereObject.radius = getValue($('#Radius', group));
|
||||
sphereObject.total = getValue($('#Total', group));
|
||||
sphereObject.pointSize = getValue($('#PointSize', group));
|
||||
sphereObject.steps = getValue($('#Steps', group));
|
||||
sphereObject.dirtyMode = $('#drawModeDirty', group).checked;
|
||||
analyser.smoothingTimeConstant = getValue($('#Smoothing', group));
|
||||
}
|
||||
|
||||
function setTranslate() {
|
||||
let group = $('#translate');
|
||||
let items = ["#X", "#Y", "#Z"];
|
||||
for (let i in items) {
|
||||
sphereObject.translate[i] = getValue($(items[i], group))
|
||||
}
|
||||
}
|
||||
|
||||
function generateSlider(fromList, min, max, value, appendTo, group, stepSize) {
|
||||
for (let i = 0; i < fromList.length; i++) {
|
||||
let label = create("label", fromList[i]);
|
||||
let reset = create("button", "d");
|
||||
let range = create("input");
|
||||
range.type = "range";
|
||||
range.min = min;
|
||||
range.max = max;
|
||||
range.value = value;
|
||||
range.id = fromList[i];
|
||||
range.step = max === 1 ? .01 : (stepSize || 1);
|
||||
range.dataset.group = group;
|
||||
range.dataset.value = value;
|
||||
let item = create("group-input");
|
||||
append(item, [label, range, reset])
|
||||
appendTo.appendChild(item);
|
||||
}
|
||||
}
|
||||
|
||||
function generateCheckBox(fromList, defaultValue, appendTo, group) {
|
||||
for (let i = 0; i < fromList.length; i++) {
|
||||
let label = create("label", fromList[i])
|
||||
let checkbox = create("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.checked = defaultValue === true;
|
||||
checkbox.dataset.group = group;
|
||||
checkbox.id = group + fromList[i].split(" ").join("-");
|
||||
let item = create("group-input");
|
||||
append(item, [label, createSwitch(checkbox)])
|
||||
appendTo.appendChild(item);
|
||||
}
|
||||
}
|
||||
|
||||
function createSwitch(checkbox) {
|
||||
let swi = create("switch");
|
||||
let label = create("label");
|
||||
label.setAttribute("for", checkbox.id);
|
||||
append(swi, [checkbox, label]);
|
||||
return swi;
|
||||
}
|
||||
|
||||
function changeHandler(el) {
|
||||
let d = el.dataset.group;
|
||||
if (d === "rotate") {
|
||||
setRotations(el);
|
||||
} else if (d === "translate") {
|
||||
setTranslate();
|
||||
} else if (d === "color") {
|
||||
setColors();
|
||||
} else if (d === "light") {
|
||||
setWorld();
|
||||
} else if (d === "drawMode") {
|
||||
setDraw();
|
||||
}
|
||||
}
|
||||
|
||||
document.body.addDelegatedEventListener('input', 'group-input input', (ev, el) => {
|
||||
changeHandler(el);
|
||||
})
|
||||
|
||||
document.body.addDelegatedEventListener('click', 'group-input button', (ev, el) => {
|
||||
let input = $('input', el.parentNode);
|
||||
input.value = input.dataset.value;
|
||||
changeHandler(input);
|
||||
});
|
||||
|
||||
function getValue(slider) {
|
||||
try {
|
||||
return parseFloat(slider.value);
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
$('.settings-icon').addEventListener('click', function () {
|
||||
$('.off-can').classList.toggle("closed");
|
||||
})
|
83
js/handler.js
Normal file
83
js/handler.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
class Shaders {
|
||||
constructor(gl) {
|
||||
this.gl = gl;
|
||||
this.shaderNames = [];
|
||||
this.shaders = {};
|
||||
this.programs = {};
|
||||
}
|
||||
|
||||
async loadShader(name, path) {
|
||||
this.shaderNames.push(name);
|
||||
await this.load(name, path + name + ".vert", this.gl.VERTEX_SHADER);
|
||||
await this.load(name, path + name + ".frag", this.gl.FRAGMENT_SHADER);
|
||||
}
|
||||
|
||||
async load(name, url, type) {
|
||||
let realName = name + "_" + type;
|
||||
if (!this.shaders[realName]) {
|
||||
let data = await fetch(url);
|
||||
let shader = this.createShader(await data.text(), type);
|
||||
if (shader) {
|
||||
this.shaders[realName] = shader;
|
||||
}
|
||||
}
|
||||
return !!this.shaders[realName];
|
||||
}
|
||||
|
||||
getShader(name, type) {
|
||||
let realName = name + "_" + type;
|
||||
return this.shaders[realName];
|
||||
}
|
||||
|
||||
getAllShaders() {
|
||||
return this.shaderNames;
|
||||
}
|
||||
|
||||
async createProgramForEach(arr) {
|
||||
arr = arr || this.shaderNames;
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
let shader = arr[i];
|
||||
let v = await shaderHandler.createProgram(shader, [shader])
|
||||
if (!v) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
createShader(source, type) {
|
||||
let gl = this.gl;
|
||||
let shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
return shader;
|
||||
}
|
||||
console.error(gl.getShaderInfoLog(shader));
|
||||
gl.deleteShader(shader);
|
||||
return null;
|
||||
}
|
||||
|
||||
createProgram(name, shaders) {
|
||||
let gl = this.gl;
|
||||
let pro = gl.createProgram();
|
||||
for (let i = 0; i < shaders.length; i++) {
|
||||
gl.attachShader(pro, this.getShader(shaders[i], gl.VERTEX_SHADER));
|
||||
gl.attachShader(pro, this.getShader(shaders[i], gl.FRAGMENT_SHADER));
|
||||
}
|
||||
gl.linkProgram(pro);
|
||||
var success = gl.getProgramParameter(pro, gl.LINK_STATUS);
|
||||
if (success) {
|
||||
this.programs[name] = pro;
|
||||
return pro;
|
||||
}
|
||||
|
||||
console.log(gl.getProgramInfoLog(pro));
|
||||
gl.deleteProgram(pro);
|
||||
return null;
|
||||
}
|
||||
|
||||
getProgram(name) {
|
||||
return this.programs[name];
|
||||
}
|
||||
}
|
53
js/index.js
Normal file
53
js/index.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
let shaderHandler, gl, c, actx, analyser, peak;
|
||||
let positionData = [];
|
||||
let positionSize = 8192 * 2 * 2;
|
||||
|
||||
function createAudioContextStream(stream) {
|
||||
let AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
if (actx) {
|
||||
actx.close();
|
||||
}
|
||||
actx = new AudioContext();
|
||||
analyser = actx.createAnalyser();
|
||||
let Source;
|
||||
|
||||
analyser.fftSize = 4096;
|
||||
analyser.maxDecibels = 0;
|
||||
analyser.smoothingTimeConstant = .4;
|
||||
Source = actx.createMediaStreamSource(stream);
|
||||
|
||||
Source.connect(analyser);
|
||||
return null;
|
||||
}
|
||||
|
||||
async function init() {
|
||||
c = document.body.querySelector('#c');
|
||||
gl = c.getContext("webgl2");
|
||||
if (!gl) {
|
||||
console.error("GL not found");
|
||||
return false;
|
||||
}
|
||||
shaderHandler = new Shaders(gl);
|
||||
await shaderHandler.loadShader("test", "shaders/");
|
||||
|
||||
//sphere shader
|
||||
await shaderHandler.loadShader("sphere", "shaders/", gl.VERTEX_SHADER);
|
||||
return shaderHandler.createProgramForEach(["test", "sphere"]);
|
||||
}
|
||||
|
||||
(function () {
|
||||
navigator.mediaDevices.getUserMedia({audio: true, video: false})
|
||||
.then(createAudioContextStream).then(e => {
|
||||
generateRotationSliders();
|
||||
generateColorSliders();
|
||||
generateWorldSliders();
|
||||
generateDrawSliders();
|
||||
generateTranslateSliders();
|
||||
init().then(b => {
|
||||
if (b) {
|
||||
sphereObject.drawMode = gl.POINTS;
|
||||
draw();
|
||||
}
|
||||
})
|
||||
});
|
||||
})();
|
88
js/old.js
Normal file
88
js/old.js
Normal file
|
@ -0,0 +1,88 @@
|
|||
function readData() {
|
||||
let items = analyser.fftSize;
|
||||
let dataArray = new Float32Array(items);
|
||||
analyser.getFloatTimeDomainData(dataArray);
|
||||
let space = 255 / (items + 2);
|
||||
let pos = [0, 127.5];
|
||||
let x = space;
|
||||
peak = 0;
|
||||
for (let i = 0; i < items; i++) {
|
||||
let data = (dataArray[i] * 125) + 127.5
|
||||
if (Math.abs(data) > peak) {
|
||||
peak = data;
|
||||
}
|
||||
pos.push(x);
|
||||
pos.push(data);
|
||||
x += space;
|
||||
}
|
||||
pos.push(255, 127.5);
|
||||
positions = pos;
|
||||
}
|
||||
|
||||
let positions = [
|
||||
Math.random(), Math.random(),
|
||||
Math.random(), Math.random(),
|
||||
Math.random(), Math.random(),
|
||||
];
|
||||
|
||||
/*function draw() {
|
||||
readData();
|
||||
let program = shaderHandler.getProgram("test");
|
||||
let positionAttributeLocation = gl.getAttribLocation(program, "a_position");
|
||||
var colorLocation = gl.getUniformLocation(program, "u_color");
|
||||
let positionBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.DYNAMIC_DRAW);
|
||||
let vao = gl.createVertexArray();
|
||||
gl.bindVertexArray(vao);
|
||||
gl.enableVertexAttribArray(positionAttributeLocation);
|
||||
gl.vertexAttribPointer(
|
||||
positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
|
||||
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.useProgram(program);
|
||||
gl.uniform4f(colorLocation, peak / 255, (255 - peak) / 255, .2, 1);
|
||||
gl.drawArrays(gl.LINE_STRIP, 0, positions.length / 2);
|
||||
|
||||
let positionBuffer2 = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer2);
|
||||
gl.uniform4f(colorLocation, 0, 0, 1, .3);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.DYNAMIC_DRAW);
|
||||
gl.drawArrays(gl.POINTS, 0, positions.length / 2);
|
||||
|
||||
requestAnimationFrame(draw);
|
||||
}*/
|
||||
|
||||
function setupSphere() {
|
||||
sphereData = [];
|
||||
let data = readData(),
|
||||
radData = data[0];
|
||||
let sin = Math.sin,
|
||||
cos = Math.cos,
|
||||
total = sphereObject.total || 50,
|
||||
cTotal = (total + 1) * (total + 1),
|
||||
r = sphereObject.radius || 1000,
|
||||
rx = r / c.width,
|
||||
ry = r / c.height,
|
||||
counter = 0;
|
||||
for (let i = 1; i <= total; i++) {
|
||||
let lat = VTUtils.map(i, 0, total, 0, Math.PI, false);
|
||||
for (let j = 1; j <= total; j++) {
|
||||
let lon = VTUtils.map(j, 0, total, 0, Math.TWO_PI, false);
|
||||
let map = VTUtils.map(counter, 0, cTotal, 0, radData.length - 1, false);
|
||||
map = Math.floor(map);
|
||||
let realRX = rx + radData[map];
|
||||
let realRY = ry + radData[map];
|
||||
let x = rx * sin(lon) * cos(lat);
|
||||
let y = rx * sin(lon) * sin(lat);
|
||||
let z = ry * cos(lon);
|
||||
sphereData.push(x);
|
||||
sphereData.push(y);
|
||||
sphereData.push(z);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
return [VTUtils.hsvToRgb(data[1], 1, 1), data[1]];
|
||||
}
|
207
js/sphere.js
Normal file
207
js/sphere.js
Normal file
|
@ -0,0 +1,207 @@
|
|||
let sphereData = [];
|
||||
Math.HALF_PI = Math.PI / 2;
|
||||
Math.TWO_PI = Math.PI * 2;
|
||||
|
||||
let sphereObject = {
|
||||
rotationRaw: [0, 0, 0], // degrees
|
||||
rotation: [0, 0, 0], //radians
|
||||
rotateInc: [0.0, 0.0, 0.0], //degreesInc
|
||||
rotateByBeat: true,
|
||||
translate: [1, 1, 1],
|
||||
total: 50,
|
||||
radius: 500,
|
||||
color: {r: 0, g: 0, b: 0},
|
||||
colorByBeat: true,
|
||||
drawMode: 0,
|
||||
sphereMode: 0,
|
||||
lightPos: [0, 0, 0],
|
||||
pointSize: 2,
|
||||
steps: 512,
|
||||
dirtyMode: false
|
||||
}
|
||||
|
||||
function readData() {
|
||||
let items = sphereObject.steps;
|
||||
let dataArray = new Uint8Array(items);
|
||||
analyser.getByteFrequencyData(dataArray);
|
||||
let arr = [];
|
||||
let sum = 0;
|
||||
for (let i = 0; i < items; i++) {
|
||||
let data = dataArray[i] / 255;
|
||||
arr.push(data * .5);
|
||||
sum += dataArray[i];
|
||||
}
|
||||
return [arr, (sum / items) / 255];
|
||||
}
|
||||
|
||||
let sphereDataVectors = [], lastTotal = 0;
|
||||
|
||||
function prepareData() {
|
||||
let total = sphereObject.total;
|
||||
if (lastTotal !== total) {
|
||||
sphereDataVectors = [];
|
||||
for (let i = 0; i <= total; i++) {
|
||||
sphereDataVectors[i] = [];
|
||||
for (let j = 0; j <= total; j++) {
|
||||
sphereDataVectors[i][j] = new VTVector();
|
||||
}
|
||||
}
|
||||
lastTotal = total;
|
||||
}
|
||||
}
|
||||
|
||||
function setupSphere() {
|
||||
sphereData = [];
|
||||
let data = readData(),
|
||||
radData = data[0],
|
||||
map = VTUtils.map,
|
||||
total = sphereObject.total,
|
||||
cTotal = (total + 1) * (total + 1),
|
||||
radius = sphereObject.radius,
|
||||
rx = radius / c.width,
|
||||
ry = radius / c.height,
|
||||
counter = 0;
|
||||
|
||||
prepareData();
|
||||
for (let i = 0; i <= total; i++) {
|
||||
let lat = map(i, 0, total, 0, Math.PI, false);
|
||||
for (let j = 0; j <= total; j++) {
|
||||
let lon = map(j, 0, total, 0, Math.TWO_PI, false);
|
||||
let rAdd = getAddRad(counter, radData, cTotal);
|
||||
let realRX = rx + rAdd;
|
||||
let realRY = ry + rAdd;
|
||||
let {x, y, z} = sphereMode(lat, lon, i, counter, realRX, realRY);
|
||||
if (sphereObject.drawMode < 2 || sphereObject.dirtyMode) {
|
||||
sphereData.push(x, y, z);
|
||||
} else {
|
||||
sphereDataVectors[i][j].setXYZ(x, y, z);
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
if (sphereObject.drawMode > 1 && !sphereObject.dirtyMode) {
|
||||
for (let i = 0; i < total; i++) {
|
||||
for (let j = 0; j <= total; j++) {
|
||||
let cur = sphereDataVectors[i][j];
|
||||
sphereData.push(cur.x, cur.y, cur.z);
|
||||
cur = sphereDataVectors[i + 1][j];
|
||||
sphereData.push(cur.x, cur.y, cur.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
return [VTUtils.peakRGB(data[1] * 2), data[1]];
|
||||
}
|
||||
|
||||
function getAddRad(counter, data, total) {
|
||||
let mapping, rAdd, map = VTUtils.map;
|
||||
if (sphereObject.sphereMode === 3) {
|
||||
let h = total / 2;
|
||||
if (counter > h) {
|
||||
mapping = map(counter, h, total, data.length - 1, 0);
|
||||
} else {
|
||||
mapping = map(counter, 0, h, 0, data.length - 1);
|
||||
}
|
||||
rAdd = data[Math.round(mapping)] || 0;
|
||||
} else {
|
||||
mapping = map(counter, 0, total, 0, data.length - 1);
|
||||
rAdd = data[Math.round(mapping)] || 0;
|
||||
}
|
||||
return rAdd;
|
||||
}
|
||||
|
||||
let position, world, color, rotate, light, lightPos, lightAngle = 90;
|
||||
|
||||
function prepare(program) {
|
||||
position = gl.getAttribLocation(program, "a_position");
|
||||
world = gl.getUniformLocation(program, 'u_world');
|
||||
color = gl.getUniformLocation(program, "u_color");
|
||||
light = gl.getUniformLocation(program, "u_light");
|
||||
rotate = gl.getUniformLocation(program, "u_matrix");
|
||||
lightPos = gl.getUniformLocation(program, "u_lightPos");
|
||||
let pointSize = gl.getUniformLocation(program, "u_pointSize");
|
||||
gl.uniformMatrix4fv(world, false, TDUtils.yRotation(TDUtils.degToRad(lightAngle)));
|
||||
gl.uniform3fv(light, [0.95, 0.62, 0.094]);
|
||||
gl.uniform3fv(lightPos, sphereObject.lightPos);
|
||||
gl.uniform1f(pointSize, sphereObject.pointSize);
|
||||
}
|
||||
|
||||
function draw() {
|
||||
c.width = window.innerWidth;
|
||||
c.height = window.innerHeight;
|
||||
let aspect = c.height / c.width;
|
||||
let d = setupSphere();
|
||||
let newColor = d[0];
|
||||
let program = shaderHandler.getProgram("sphere");
|
||||
gl.useProgram(program);
|
||||
prepare(program);
|
||||
let matrix = [
|
||||
sphereObject.translate[0] / aspect, 0, 0, 0,
|
||||
0, sphereObject.translate[1], 0, 0,
|
||||
0, 0, sphereObject.translate[2], 0,
|
||||
0, 0, 0, 1
|
||||
]
|
||||
matrix = TDUtils.multiply(matrix, TDUtils.xRotation(sphereObject.rotation[0]));
|
||||
matrix = TDUtils.multiply(matrix, TDUtils.yRotation(sphereObject.rotation[1]));
|
||||
matrix = TDUtils.multiply(matrix, TDUtils.zRotation(sphereObject.rotation[2]));
|
||||
gl.uniformMatrix4fv(rotate, false, matrix);
|
||||
|
||||
let positionBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(sphereData), gl.DYNAMIC_DRAW);
|
||||
let vao = gl.createVertexArray();
|
||||
gl.bindVertexArray(vao);
|
||||
gl.enableVertexAttribArray(position);
|
||||
gl.vertexAttribPointer(position, 3, gl.FLOAT, true, 0, 0);
|
||||
|
||||
gl.clearColor(0, 0, 0, 1);
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.depthFunc(gl.LEQUAL);
|
||||
gl.clearDepth(2.0)
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
|
||||
if (sphereObject.colorByBeat) {
|
||||
gl.uniform4f(color, newColor.r, newColor.g, 0, 1);
|
||||
} else {
|
||||
let cx = sphereObject.color;
|
||||
gl.uniform4f(color, cx.r, cx.g, cx.b, 1);
|
||||
}
|
||||
gl.drawArrays(sphereObject.drawMode || gl.POINTS, 0, sphereData.length / 3);
|
||||
if (sphereObject.rotateByBeat) {
|
||||
sphereObject.rotationRaw[1] += d[1];
|
||||
sphereObject.rotationRaw[0] += newColor.r;
|
||||
} else {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
sphereObject.rotationRaw[i] += sphereObject.rotateInc[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
sphereObject.rotation[i] = TDUtils.degToRad(sphereObject.rotationRaw[i])
|
||||
}
|
||||
requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
function sphereMode(lat, lon, i, counter, rx, ry) {
|
||||
let x, y, z,
|
||||
sin = Math.sin,
|
||||
cos = Math.cos;
|
||||
switch (sphereObject.sphereMode) {
|
||||
case 0:
|
||||
case 3:
|
||||
x = rx * sin(lat) * cos(lon);
|
||||
y = ry * sin(lat) * sin(lon);
|
||||
z = ry * cos(lat);
|
||||
break;
|
||||
case 1:
|
||||
x = rx * sin(lon) * cos(lat);
|
||||
y = ry * sin(lon) * sin(lat);
|
||||
z = ry * cos(lat);
|
||||
break;
|
||||
case 2:
|
||||
x = rx * sin(lat) * cos(lat);
|
||||
y = ry * sin(lat) * sin(lat);
|
||||
z = ry * cos(lon);
|
||||
break;
|
||||
}
|
||||
return {x: x, y: y, z: z};
|
||||
}
|
238
js/utils.js
Normal file
238
js/utils.js
Normal file
|
@ -0,0 +1,238 @@
|
|||
class VTUtils {
|
||||
static random(min, max) {
|
||||
let rand = Math.random();
|
||||
if (typeof min === 'undefined') {
|
||||
return rand;
|
||||
} else if (typeof max === 'undefined') {
|
||||
if (min instanceof Array) {
|
||||
return min[Math.floor(rand * min.length)];
|
||||
} else {
|
||||
return rand * min;
|
||||
}
|
||||
} else {
|
||||
if (min > max) {
|
||||
let tmp = min;
|
||||
min = max;
|
||||
max = tmp;
|
||||
}
|
||||
return rand * (max - min) + min;
|
||||
}
|
||||
};
|
||||
|
||||
static randomInt(min, max) {
|
||||
return Math.floor(VTUtils.random(min, max));
|
||||
}
|
||||
|
||||
static normalize(val, max, min) {
|
||||
return (val - min) / (max - min);
|
||||
};
|
||||
|
||||
static distance(x, y, x2, y2) {
|
||||
let a = x - x2;
|
||||
let b = y - y2;
|
||||
|
||||
return Math.sqrt(a * a + b * b);
|
||||
}
|
||||
|
||||
static map(n, start1, stop1, start2, stop2, withinBounds) {
|
||||
let newVal = (n - start1) / (stop1 - start1) * (stop2 - start2) + start2;
|
||||
if (!withinBounds) {
|
||||
return newVal;
|
||||
}
|
||||
if (start2 < stop2) {
|
||||
return this.constrain(newVal, start2, stop2);
|
||||
} else {
|
||||
return this.constrain(newVal, stop2, start2);
|
||||
}
|
||||
};
|
||||
|
||||
static constrain(n, low, high) {
|
||||
return Math.max(Math.min(n, high), low);
|
||||
}
|
||||
|
||||
static hsvToRgb(h, s, v) {
|
||||
var r, g, b;
|
||||
|
||||
var i = Math.floor(h * 6);
|
||||
var f = h * 6 - i;
|
||||
var p = v * (1 - s);
|
||||
var q = v * (1 - f * s);
|
||||
var 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: r, g: g, b: b};
|
||||
}
|
||||
|
||||
static peakRGB(peak) {
|
||||
return {
|
||||
r: peak,
|
||||
g: 1 - peak,
|
||||
b: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class VTVector {
|
||||
constructor(x, y, z) {
|
||||
this.x = x || 0;
|
||||
this.y = y || 0;
|
||||
this.z = z || 0;
|
||||
}
|
||||
|
||||
//helper
|
||||
static createRandom(x, y, z) {
|
||||
x = x || 1;
|
||||
y = y || 1;
|
||||
z = z || 0;
|
||||
return new VTVector(VTUtils.random(-x, x), VTUtils.random(-y, y), VTUtils.random(-z, z));
|
||||
}
|
||||
|
||||
mult(times) {
|
||||
this.x *= times;
|
||||
this.y *= times;
|
||||
this.z *= times;
|
||||
}
|
||||
|
||||
set(vector) {
|
||||
this.x = vector.x;
|
||||
this.y = vector.y;
|
||||
this.z = vector.z;
|
||||
}
|
||||
|
||||
add(vector) {
|
||||
this.x = this.x + vector.x;
|
||||
this.y = this.y + vector.y;
|
||||
this.z = this.z + vector.z;
|
||||
}
|
||||
|
||||
addXYZ(x, y, z) {
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
this.z += z;
|
||||
}
|
||||
|
||||
setXYZ(x,y,z) {
|
||||
this.x = x || 0;
|
||||
this.y = y || 0;
|
||||
this.z = z || 0;
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new VTVector(this.x, this.y, this.z);
|
||||
}
|
||||
}
|
||||
|
||||
class TDUtils {
|
||||
static multiply(a, b) {
|
||||
let b00 = b[0 * 4 + 0];
|
||||
let b01 = b[0 * 4 + 1];
|
||||
let b02 = b[0 * 4 + 2];
|
||||
let b03 = b[0 * 4 + 3];
|
||||
let b10 = b[1 * 4 + 0];
|
||||
let b11 = b[1 * 4 + 1];
|
||||
let b12 = b[1 * 4 + 2];
|
||||
let b13 = b[1 * 4 + 3];
|
||||
let b20 = b[2 * 4 + 0];
|
||||
let b21 = b[2 * 4 + 1];
|
||||
let b22 = b[2 * 4 + 2];
|
||||
let b23 = b[2 * 4 + 3];
|
||||
let b30 = b[3 * 4 + 0];
|
||||
let b31 = b[3 * 4 + 1];
|
||||
let b32 = b[3 * 4 + 2];
|
||||
let b33 = b[3 * 4 + 3];
|
||||
let a00 = a[0 * 4 + 0];
|
||||
let a01 = a[0 * 4 + 1];
|
||||
let a02 = a[0 * 4 + 2];
|
||||
let a03 = a[0 * 4 + 3];
|
||||
let a10 = a[1 * 4 + 0];
|
||||
let a11 = a[1 * 4 + 1];
|
||||
let a12 = a[1 * 4 + 2];
|
||||
let a13 = a[1 * 4 + 3];
|
||||
let a20 = a[2 * 4 + 0];
|
||||
let a21 = a[2 * 4 + 1];
|
||||
let a22 = a[2 * 4 + 2];
|
||||
let a23 = a[2 * 4 + 3];
|
||||
let a30 = a[3 * 4 + 0];
|
||||
let a31 = a[3 * 4 + 1];
|
||||
let a32 = a[3 * 4 + 2];
|
||||
let a33 = a[3 * 4 + 3];
|
||||
let dst = [];
|
||||
dst[0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30;
|
||||
dst[1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31;
|
||||
dst[2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32;
|
||||
dst[3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33;
|
||||
dst[4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30;
|
||||
dst[5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31;
|
||||
dst[6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32;
|
||||
dst[7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33;
|
||||
dst[8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30;
|
||||
dst[9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31;
|
||||
dst[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32;
|
||||
dst[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33;
|
||||
dst[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30;
|
||||
dst[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31;
|
||||
dst[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32;
|
||||
dst[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33;
|
||||
return dst;
|
||||
}
|
||||
|
||||
static xRotation(angleInRadians) {
|
||||
let c = Math.cos(angleInRadians);
|
||||
let s = Math.sin(angleInRadians);
|
||||
|
||||
return [
|
||||
1, 0, 0, 0,
|
||||
0, c, s, 0,
|
||||
0, -s, c, 0,
|
||||
0, 0, 0, 1,
|
||||
];
|
||||
}
|
||||
|
||||
static yRotation(angleInRadians) {
|
||||
let c = Math.cos(angleInRadians);
|
||||
let s = Math.sin(angleInRadians);
|
||||
|
||||
return [
|
||||
c, 0, -s, 0,
|
||||
0, 1, 0, 0,
|
||||
s, 0, c, 0,
|
||||
0, 0, 0, 1,
|
||||
];
|
||||
}
|
||||
|
||||
static zRotation(angleInRadians) {
|
||||
let c = Math.cos(angleInRadians);
|
||||
let s = Math.sin(angleInRadians);
|
||||
|
||||
return [
|
||||
c, s, 0, 0,
|
||||
-s, c, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1,
|
||||
];
|
||||
}
|
||||
|
||||
static degToRad(d) {
|
||||
return d * Math.PI / 180;
|
||||
}
|
||||
}
|
19
shaders/sphere.frag
Normal file
19
shaders/sphere.frag
Normal file
|
@ -0,0 +1,19 @@
|
|||
#version 300 es
|
||||
|
||||
// fragment shaders don't have a default precision so we need
|
||||
// to pick one. mediump is a good default. It means "medium precision"
|
||||
precision highp float;
|
||||
|
||||
in vec3 v_surfaceToLight;
|
||||
|
||||
uniform vec4 u_color;
|
||||
uniform vec3 u_lightPos;
|
||||
|
||||
out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
vec3 surfaceToLightDirection = normalize(v_surfaceToLight);
|
||||
outColor = u_color;
|
||||
float light = 1.0;
|
||||
//outColor.rgb *= surfaceToLightDirection;
|
||||
}
|
21
shaders/sphere.vert
Normal file
21
shaders/sphere.vert
Normal file
|
@ -0,0 +1,21 @@
|
|||
#version 300 es
|
||||
|
||||
in vec4 a_position;
|
||||
uniform mat4 u_world;
|
||||
uniform mat4 u_matrix;
|
||||
uniform vec3 u_lightPos;
|
||||
uniform vec3 u_light;
|
||||
uniform float u_pointSize;
|
||||
|
||||
out vec3 v_surfaceToLight;
|
||||
|
||||
void main() {
|
||||
// convert the position from pixels to 0.0 to 1.0
|
||||
vec4 pos = a_position * u_matrix;
|
||||
gl_Position = pos;
|
||||
gl_PointSize = u_pointSize;
|
||||
|
||||
vec3 surfaceWorldPosition = (u_world * pos).xyz;
|
||||
|
||||
v_surfaceToLight = u_lightPos - surfaceWorldPosition;
|
||||
}
|
13
shaders/test.frag
Normal file
13
shaders/test.frag
Normal file
|
@ -0,0 +1,13 @@
|
|||
#version 300 es
|
||||
|
||||
// fragment shaders don't have a default precision so we need
|
||||
// to pick one. mediump is a good default. It means "medium precision"
|
||||
precision mediump float;
|
||||
|
||||
uniform vec4 u_color;
|
||||
|
||||
out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = u_color;
|
||||
}
|
11
shaders/test.vert
Normal file
11
shaders/test.vert
Normal file
|
@ -0,0 +1,11 @@
|
|||
#version 300 es
|
||||
|
||||
in vec2 a_position;
|
||||
void main() {
|
||||
// convert the position from pixels to 0.0 to 1.0
|
||||
vec2 scale = a_position / vec2(255.0, 255.0);
|
||||
vec2 remap = scale * 2.0;
|
||||
vec2 space = remap - 1.0;
|
||||
space.y = space.y * 0.85;
|
||||
gl_Position = vec4(space, 0,1);
|
||||
}
|
215
style.css
Normal file
215
style.css
Normal file
|
@ -0,0 +1,215 @@
|
|||
html, body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
font-size: 16px;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div {
|
||||
position: fixed;
|
||||
color: #fff;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#c {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.settings-icon {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
font-size: 1.5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.settings-icon:hover {
|
||||
color: #0199ff;
|
||||
}
|
||||
|
||||
.off-can {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 300px;
|
||||
background-color: rgba(33, 33, 33, 0.6);
|
||||
height: 100vh;
|
||||
transition: all .5s;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
group {
|
||||
display: block;
|
||||
padding-bottom: 10px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
group-label {
|
||||
display: block;
|
||||
border-bottom: 1px solid #ffffff;
|
||||
font-size: 21px;
|
||||
font-weight: 500;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
group-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
group-input label {
|
||||
padding-right: 10px;
|
||||
user-select: none;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
|
||||
group-input input {
|
||||
flex-grow: 1;
|
||||
user-select: none;
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
group-input button {
|
||||
border: 1px solid #dcdcdc;
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.closed {
|
||||
transform: translateX(-350px);
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
input[type=range] {
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input[type=range]:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type=range]::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 25.6px;
|
||||
cursor: pointer;
|
||||
box-shadow: 1px 1px 1px #000000, 0 0 1px #0d0d0d;
|
||||
background: #424242;
|
||||
border-radius: 0;
|
||||
border: 0 solid #010101;
|
||||
}
|
||||
|
||||
input[type=range]::-webkit-slider-thumb {
|
||||
box-shadow: 0 0 0 #470000, 0 0 0 #610000;
|
||||
border: 0 solid #ff0000;
|
||||
height: 25px;
|
||||
width: 15px;
|
||||
border-radius: 0;
|
||||
background: #a8c64e;
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
margin-top: 0.3px;
|
||||
}
|
||||
|
||||
input[type=range]:focus::-webkit-slider-runnable-track {
|
||||
background: #545454;
|
||||
}
|
||||
|
||||
input[type=range]::-moz-range-track {
|
||||
width: 100%;
|
||||
height: 25.6px;
|
||||
cursor: pointer;
|
||||
box-shadow: 1px 1px 1px #000000, 0 0 1px #0d0d0d;
|
||||
background: #424242;
|
||||
border-radius: 0;
|
||||
border: 0 solid #010101;
|
||||
}
|
||||
|
||||
input[type=range]::-moz-range-thumb {
|
||||
box-shadow: 0 0 0 #470000, 0 0 0 #610000;
|
||||
border: 0 solid #ff0000;
|
||||
height: 25px;
|
||||
width: 15px;
|
||||
border-radius: 0;
|
||||
background: #a8c64e;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type=range]::-ms-track {
|
||||
width: 100%;
|
||||
height: 25.6px;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
input[type=range]::-ms-fill-lower {
|
||||
background: #303030;
|
||||
border: 0 solid #010101;
|
||||
border-radius: 0;
|
||||
box-shadow: 1px 1px 1px #000000, 0 0 1px #0d0d0d;
|
||||
}
|
||||
|
||||
input[type=range]::-ms-fill-upper {
|
||||
background: #424242;
|
||||
border: 0 solid #010101;
|
||||
border-radius: 0;
|
||||
box-shadow: 1px 1px 1px #000000, 0 0 1px #0d0d0d;
|
||||
}
|
||||
|
||||
input[type=range]::-ms-thumb {
|
||||
box-shadow: 0 0 0 #470000, 0 0 0 #610000;
|
||||
border: 0 solid #ff0000;
|
||||
width: 15px;
|
||||
border-radius: 0;
|
||||
background: #a8c64e;
|
||||
cursor: pointer;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
input[type=range]:focus::-ms-fill-lower {
|
||||
background: #424242;
|
||||
}
|
||||
|
||||
input[type=range]:focus::-ms-fill-upper {
|
||||
background: #545454;
|
||||
}
|
||||
|
||||
switch input {
|
||||
position: absolute;
|
||||
appearance: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
switch label {
|
||||
display: block;
|
||||
border-radius: 10px;
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
background-color: #dcdcdc;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
switch label:after {
|
||||
content: '';
|
||||
background-color: #ff3232;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
border-radius: 10px;
|
||||
transition: .5s;
|
||||
}
|
||||
|
||||
switch input:checked + label:after {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
Loading…
Reference in a new issue