Created
December 23, 2021 02:23
-
-
Save ECHibiki/5841bc95b7cc37400d514b7b9b3f92d5 to your computer and use it in GitHub Desktop.
Particle Emitter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<strong>Click to trigger particles</strong><br /> | |
<p>Stars Created : <span id="stars">0</span></p> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix.js"></script> | |
<audio crossOrigin="anonymous" id="audio" src="https://kissu.moe/static/sounds/60t3mf.mp3" controls></audio><br /> | |
<canvas oncontextmenu="return false;" id="canvas" width="1159" height="777"></canvas><br /> | |
<p>Audio Source: Some Tekken 7 Lucky Chloe thing<br /> | |
Image Source: https://twitter.com/asa_410st/status/1472023559933415426</p> | |
<img id="test" /> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const IMAGE_SOURCE = | |
"https://kissu.moe/static/themes/694211f92274f299466b558ab027046b.jpg"; | |
const PARTICLE_SOURCE = "https://kissu.moe/static/themes/star.png"; | |
const WIDTH = 1159; | |
const HEIGHT = 777; | |
const GRAVITY = -0.001; | |
var bloom_active = 2; | |
// boilerplate from https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/ | |
// Vertex shader program | |
const generic_vshader = `#version 300 es | |
in vec4 aVertexPosition; | |
in vec2 aTextureCoord; | |
out mediump vec2 vTextureCoord; | |
void main() { | |
gl_Position = aVertexPosition; | |
vTextureCoord = aTextureCoord; | |
} | |
`; | |
const instance_vshader = `#version 300 es | |
in vec4 aVertexPosition; | |
in vec2 aTextureCoord; | |
uniform mediump float aVertexScaling; | |
uniform vec4 aVertexTranslation; | |
uniform mat4 aVertexRotation; | |
out highp vec2 vTextureCoord; | |
void main() { | |
gl_Position = aVertexRotation * aVertexPosition * vec4(aVertexScaling , aVertexScaling , 1.0 , 1.0) + aVertexTranslation; | |
vTextureCoord = aTextureCoord; | |
} | |
`; | |
const texture_shader = `#version 300 es | |
in mediump vec2 vTextureCoord; | |
uniform sampler2D uSampler; | |
out mediump vec4 fragColor; | |
void main(){ | |
fragColor = texture(uSampler , vTextureCoord); | |
} | |
`; | |
function initAudio() { | |
var audio = document.getElementById("audio"); | |
var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); | |
var source = audioCtx.createMediaElementSource(audio); | |
var analyser = audioCtx.createAnalyser(); | |
analyser.fftSize = 2048; | |
// Create a gain node | |
var gainNode = audioCtx.createGain(); | |
gainNode.gain.value = 1.0; | |
source.connect(gainNode); | |
gainNode.connect(audioCtx.destination); | |
source.connect(analyser); | |
return { audioCtx: audioCtx, analyser: analyser }; | |
} | |
// | |
// Initialize a texture and load an image. | |
// When the image finished loading copy it into the texture. | |
// | |
function loadTexture(gl, url) { | |
const texture = gl.createTexture(); | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
// Because images have to be downloaded over the internet | |
// they might take a moment until they are ready. | |
// Until then put a single pixel in the texture so we can | |
// use it immediately. When the image has finished downloading | |
// we'll update the texture with the contents of the image. | |
const level = 0; | |
const internalFormat = gl.RGBA; | |
const width = 1; | |
const height = 1; | |
const border = 0; | |
const srcFormat = gl.RGBA; | |
const srcType = gl.UNSIGNED_BYTE; | |
const pixel = new Uint8Array([200, 200, 255, 255]); // opaque blue | |
gl.texImage2D( | |
gl.TEXTURE_2D, | |
level, | |
internalFormat, | |
width, | |
height, | |
border, | |
srcFormat, | |
srcType, | |
pixel | |
); | |
const image = new Image(); | |
image.crossOrigin = "Anonymous"; | |
image.onload = function () { | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
gl.texImage2D( | |
gl.TEXTURE_2D, | |
level, | |
internalFormat, | |
srcFormat, | |
srcType, | |
image | |
); | |
// WebGL1 has different requirements for power of 2 images | |
// vs non power of 2 images so check if the image is a | |
// power of 2 in both dimensions. | |
if (isPowerOf2(image.width) && isPowerOf2(image.height)) { | |
// Yes, it's a power of 2. Generate mips. | |
gl.generateMipmap(gl.TEXTURE_2D); | |
} else { | |
// No, it's not a power of 2. Turn off mips and set | |
// wrapping to clamp to edge | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); | |
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); | |
} | |
}; | |
image.src = url; | |
return texture; | |
} | |
function isPowerOf2(value) { | |
return (value & (value - 1)) == 0; | |
} | |
function loadShader(gl, type, source) { | |
const shader = gl.createShader(type); | |
// Send the source to the shader object | |
gl.shaderSource(shader, source); | |
// Compile the shader program | |
gl.compileShader(shader); | |
// See if it compiled successfully | |
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { | |
alert( | |
"An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader) | |
); | |
gl.deleteShader(shader); | |
return null; | |
} | |
return shader; | |
} | |
function initBuffers(gl) { | |
// Create a buffer for the square's positions. | |
const positionBuffer = gl.createBuffer(); | |
// Select the positionBuffer as the one to apply buffer | |
// operations to from here out. | |
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); | |
// Now create an array of positions for the square. | |
const positions = [-1.0, 1.0, | |
1.0, 1.0, | |
-1.0, -1.0, | |
1.0, -1.0]; | |
// Now pass the list of positions into WebGL to build the | |
// shape. We do this by creating a Float32Array from the | |
// JavaScript array, then use it to fill the current buffer. | |
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); | |
const indexBuffer = gl.createBuffer(); | |
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); | |
// This array defines each face as two triangles, using the | |
// indices into the vertex array to specify each triangle's | |
// position. | |
const indices = [ | |
0, 1, 2, 1, 2, 3 // front | |
]; | |
// Now send the element array to GL | |
gl.bufferData( | |
gl.ELEMENT_ARRAY_BUFFER, | |
new Uint16Array(indices), | |
gl.STATIC_DRAW | |
); | |
const textureCoordBuffer = gl.createBuffer(); | |
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer); | |
const textureCoordinates = [ | |
// Front | |
0.0, | |
0.0, | |
1.0, | |
0.0, | |
0.0, | |
1.0, | |
1.0, | |
1.0 | |
]; | |
gl.bufferData( | |
gl.ARRAY_BUFFER, | |
new Float32Array(textureCoordinates), | |
gl.STATIC_DRAW | |
); | |
return { | |
position: positionBuffer, | |
textureCoord: textureCoordBuffer, | |
indices: indexBuffer | |
}; | |
} | |
// init() is a reusable and minimable webgl initialization | |
// initBuffers and loadShader should be implemented(or copied from here) | |
function init() { | |
const canvas = document.getElementById("canvas"); | |
// Initialize the GL context | |
const gl = canvas.getContext("webgl2"); | |
// Only continue if WebGL is available and working | |
if (!gl) { | |
alert( | |
"Unable to initialize WebGL. Your browser or machine may not support it." | |
); | |
return; | |
} | |
console.log(gl.getParameter(gl.SHADING_LANGUAGE_VERSION)); | |
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); | |
gl.enable(gl.BLEND); | |
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, generic_vshader); | |
const instanceVertexShader = loadShader(gl, gl.VERTEX_SHADER, instance_vshader); | |
const textureShader = loadShader(gl, gl.FRAGMENT_SHADER, texture_shader); | |
// Create the shader program | |
const textureProgram = gl.createProgram(); | |
gl.attachShader(textureProgram, vertexShader); | |
gl.attachShader(textureProgram, textureShader); | |
gl.linkProgram(textureProgram); | |
if (!gl.getProgramParameter(textureProgram, gl.LINK_STATUS)) { | |
alert( | |
"Unable to initialize the shader textureProgram: " + | |
gl.getProgramInfoLog(textureProgram) | |
); | |
return null; | |
} | |
// Create the shader program | |
const instanceProgram = gl.createProgram(); | |
gl.attachShader(instanceProgram, instanceVertexShader); | |
gl.attachShader(instanceProgram, textureShader); | |
gl.linkProgram(instanceProgram); | |
if (!gl.getProgramParameter(instanceProgram, gl.LINK_STATUS)) { | |
alert( | |
"Unable to initialize the shader instanceProgram: " + | |
gl.getProgramInfoLog(instanceProgram) | |
); | |
return null; | |
} | |
var background_buffers = initBuffers(gl); | |
var particle_buffers = initBuffers(gl); | |
const buffers = { | |
background: background_buffers, | |
particle: particle_buffers | |
}; | |
var background_texture = loadTexture(gl, IMAGE_SOURCE); | |
var particle_texture = loadTexture(gl, PARTICLE_SOURCE); | |
const textures = { | |
background: background_texture, | |
particle: particle_texture | |
}; | |
const programInfo = { | |
program: { | |
textureProgram: textureProgram, | |
instanceProgram: instanceProgram, | |
}, | |
shaders: { | |
vertexShader: vertexShader, | |
textureShader: textureShader | |
}, | |
attribLocations: { | |
vertexPosition: gl.getAttribLocation(textureProgram, "aVertexPosition"), | |
instancePosition: gl.getAttribLocation(instanceProgram, "aVertexPosition"), | |
textureCoord: gl.getAttribLocation(textureProgram, "aTextureCoord"), | |
instanceCoord: gl.getAttribLocation(instanceProgram, "aTextureCoord"), | |
}, | |
uniformLocations: { | |
// instanceScaling: gl.getUniformLocation(instanceProgram, "aVertexScaling"), | |
// instanceRotation: gl.getUniformLocation(instanceProgram, "aVertexRotation"), | |
// instanceTranslation: gl.getUniformLocation(instanceProgram, "aVertexTranslation"), | |
instanceScaling: gl.getUniformLocation(instanceProgram, "aVertexScaling"), | |
instanceRotation: gl.getUniformLocation(instanceProgram, "aVertexRotation"), | |
instanceTranslation: gl.getUniformLocation(instanceProgram, "aVertexTranslation"), | |
sampler: gl.getUniformLocation(textureProgram, "uSampler") , | |
instanceSampler: gl.getUniformLocation(instanceProgram, "uSampler") , | |
} | |
}; | |
// particles should be created in a +z | |
var flat_emitter = createParticleEmitter( { angle: 0.0 , rotation:0.0, position: vec4.fromValues( 0.0 , 1.05, 0.0 , 0.0 ) , length: 2.0 , inverse_normal:true} ); | |
var circle_emitter = createParticleEmitter({ angle: 360.0 , rotation:0.0, position: vec4.fromValues( 0.0 , 0.0, 0.0 , 0.0 ) , length: 0.5 , inverse_normal:true} ); | |
const emmitters = { | |
flat: flat_emitter, | |
circle: circle_emitter | |
} | |
var particle_properties = [ | |
{ | |
location: vec4.fromValues(0.0,0.0,0.0,0.0),// where it is now | |
velocity: vec4.fromValues(0.0,0.0,0.0,0.0), // how fast it is moving | |
expires: 100000000000000, // a value showing how many miliseconds it has left to live | |
rotational_velocity: 1.0, // how it's rotating | |
rotation: 1.0, //rotational position | |
scaling: 0.5, // how big it is | |
inverse_normal: true // reverse normals | |
} | |
]; | |
document.getElementById("canvas").onmousedown = function (e) { | |
const rect = canvas.getBoundingClientRect() | |
const x = event.clientX - rect.left; | |
const y = event.clientY - rect.top; | |
particle_properties = particle_properties.concat( circle_emitter.createParticles(50 , | |
{ | |
additional_position: vec4.fromValues( x / (WIDTH / 2) - 1 , 1 - y / (HEIGHT / 2), 0.0 , 0.0 ) , | |
} | |
) | |
); | |
document.getElementById("stars").textContent = parseInt(document.getElementById("stars").textContent) + 50; | |
}; | |
var audio_obj = initAudio(); | |
//audio_obj.audioCtx | |
//audio_obj.analyser | |
setInterval(function () { | |
var audio_bands = audiovisualFX(audio_obj); | |
// from audio bands create particles | |
{ | |
if(audio_bands[0] + audio_bands[1] + audio_bands[2] + audio_bands[3] + audio_bands[4] + audio_bands[5] > 2.3){ | |
console.log(audio_bands[0] + audio_bands[1] + audio_bands[2] + audio_bands[3] + audio_bands[4] + audio_bands[5]) | |
document.getElementById("stars").textContent = parseInt(document.getElementById("stars").textContent) + 1; | |
particle_properties = particle_properties.concat(flat_emitter.createParticles( 2 , { | |
expiration : 10000, | |
velocity_mod: 0, | |
} ) ); | |
} | |
} | |
// limit active particles to infinity , trim oldest | |
moveParticles(particle_properties); | |
draw(gl, programInfo, buffers, textures, emmitters, audio_bands , particle_properties); | |
}, 16); | |
} | |
function createParticleEmitter(emitter_settings){ | |
// must be radians | |
const ARC_ANGLE = emitter_settings.angle; | |
const ROTATION = emitter_settings.rotation; | |
const EMITTER_LEN = emitter_settings.length; | |
// half way point of segment laid flat(0 angle). not a focal point | |
const EMITTER_POSITION = emitter_settings.position; | |
// allows for emitters to fire internally into arcs or easier reversing in general | |
const INVERSE_NORMALS = emitter_settings.inverse_normal; | |
var rotationMatrix = mat4.create() | |
mat4.rotate(rotationMatrix, // destination matrix | |
rotationMatrix, // matrix to rotate | |
ROTATION, // amount to rotate in radians | |
[0, 0, 1]); | |
// division by 0 impossible so use a different system | |
var normal_vector_fn = function(){ return vec4.fromValues(0.0 , 0.0 , 0.0, 0.0);} | |
var emission_fn = function(){} | |
if (ARC_ANGLE == 0.0) { | |
// normal vector of line is easy, and rotate it by rotation value | |
normal_vector_fn = function(){ | |
var norm = vec4.create(); | |
mat4.multiply( norm , rotationMatrix , vec4.fromValues( 0.0 , 1.0 , 0.0 , 0.0)); | |
vec4.scale( norm , norm , (INVERSE_NORMALS ? -1 : 1)) | |
return norm; | |
} | |
// set the position of a particle | |
emission_fn = function(position_segment){ | |
// pick a position on a centered line, then rotate it | |
var distance = position_segment * EMITTER_LEN - EMITTER_LEN / 2.0; | |
var position = vec4.create(); | |
mat4.multiply( position , rotationMatrix , vec4.fromValues( distance , 0.0 , 0.0 , 0.0 )); | |
vec4.add(position , position , EMITTER_POSITION) | |
return position; | |
} | |
} else{ | |
// grab the center | |
var radius_len = EMITTER_LEN / ARC_ANGLE; | |
// polar coordinates can determine cartesian coordinates easily. | |
// Normal vectors will be the circle radius vector. Distance from center determined by radius vector. | |
normal_vector_fn = function(arc_segment){ | |
// arc segment is a 0.0 to 1.0 value representing the distace down the arc it is | |
// theta is bound to be a ratio of the max angle permitted. Create the arc then rotate it to be centered | |
var theta = arc_segment * ARC_ANGLE + Math.PI / 2.0 - ARC_ANGLE / 2.0; | |
var norm = vec4.create(); | |
mat4.multiply( norm , rotationMatrix , vec4.fromValues( Math.cos(theta) , Math.sin(theta) , 0.0 , 0.0)); | |
vec4.scale( norm , norm , (INVERSE_NORMALS ? -1 : 1)) | |
return norm ; | |
} | |
emission_fn = function(arc_segment){ | |
// pick a position on a centered line, then rotate it | |
var theta = arc_segment * ARC_ANGLE + Math.PI / 2.0 - ARC_ANGLE / 2.0; | |
var position = vec4.create(); | |
mat4.multiply( position , rotationMatrix , vec4.fromValues( radius_len * Math.cos(theta) , radius_len * Math.sin(theta) , 0.0 , 0.0)); | |
vec4.add(position , position , EMITTER_POSITION) | |
return position; | |
} | |
} | |
return { | |
createParticles: function(quanitity , additional_obj){ | |
if(!additional_obj){ | |
additional_obj = new Object(); | |
} | |
var new_particles = [ | |
// { | |
// location: , // where it is now | |
// velocity:, // how fast it is moving | |
// expires: , // a value showing how many miliseconds it has left to live | |
// rotational_velocity: , // how it's rotating | |
// rotation: , //rotational position | |
// scaling: // how big it is | |
// } | |
]; | |
for (var p = 0 ; p < quanitity ; p++){ | |
var arc_rand = Math.random(); | |
var velocity_rand = Math.random() * 0.125 * (additional_obj.velocity_mod != undefined ? additional_obj.velocity_mod : 1.0); | |
var particle_location = vec4.create(); | |
new_particles.push( | |
{ | |
location: (additional_obj.additional_position ? vec4.add(particle_location , emission_fn(arc_rand) , additional_obj.additional_position ) : emission_fn(arc_rand)), // where it is now | |
velocity: vec4.multiply(normal_vector_fn(arc_rand) , normal_vector_fn(arc_rand) , vec4.fromValues(velocity_rand , velocity_rand , 0.0 , 0.0) ) , // how fast it is moving | |
expires: Date.now() + (additional_obj.expiration ? additional_obj.expiration : 2000), // a value showing how many miliseconds it has left to live | |
rotational_velocity: Math.random() * Math.PI / 20, // how it's rotating | |
rotation: Math.random() * 2 * Math.PI, //rotational position | |
scaling: Math.random() * (0.1 - 0.01) + 0.01 | |
}); | |
} | |
return new_particles; | |
} | |
} | |
} | |
function moveParticles(particle_properties){ | |
var removal_indices = []; | |
var skip_count = particle_properties.length - 500000; | |
particle_properties.forEach(function(particle, index) { | |
if(particle.expires < Date.now() || index < skip_count) { | |
removal_indices.push(index); | |
return; | |
} | |
particle_properties[index].rotation += particle_properties[index].rotational_velocity; | |
vec4.add(particle_properties[index].location , particle_properties[index].location, particle_properties[index].velocity); | |
vec4.add(particle_properties[index].velocity , particle_properties[index].velocity, vec4.fromValues(0.0, GRAVITY, 0.0, 0.0)); | |
}); | |
removal_indices.reverse().forEach(function(indice){ | |
particle_properties.splice(indice , 1); | |
}); | |
return particle_properties; | |
} | |
function draw(gl, programInfo, buffers, textures, emmitters, audio_bands , particle_properties) { | |
gl.clearColor(0.75, 0.75, 0.75, 1.0); // Clear to black, fully opaque | |
gl.clearDepth(1.0); // Clear everything | |
gl.enable(gl.DEPTH_TEST); // Enable depth testing | |
gl.depthFunc(gl.LEQUAL); // Near things obscure far things | |
// Clear the canvas before we start drawing on it. | |
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); | |
drawBackground(gl, programInfo, buffers.background, textures.background); | |
drawParticles(gl, programInfo, buffers.particle, textures.particle , particle_properties); | |
} | |
function drawBackground(gl, programInfo, buffers, texture){ | |
// Tell WebGL to use our program when drawing | |
gl.useProgram(programInfo.program.textureProgram); | |
{ | |
const numComponents = 2; // pull out 2 values per iteration | |
const type = gl.FLOAT; // the data in the buffer is 32bit floats | |
const normalize = false; // don't normalize | |
const stride = 0; // how many bytes to get from one set of values to the next | |
// 0 = use type and numComponents above | |
const offset = 0; // how many bytes inside the buffer to start from | |
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); | |
gl.vertexAttribPointer( | |
programInfo.attribLocations.vertexPosition, | |
numComponents, | |
type, | |
normalize, | |
stride, | |
offset); | |
gl.enableVertexAttribArray( | |
programInfo.attribLocations.vertexPosition); | |
} | |
// tell webgl how to pull out the texture coordinates from buffer | |
{ | |
const num = 2; // every coordinate composed of 2 values | |
const type = gl.FLOAT; // the data in the buffer is 32 bit float | |
const normalize = false; // don't normalize | |
const stride = 0; // how many bytes to get from one set to the next | |
const offset = 0; // how many bytes inside the buffer to start from | |
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.textureCoord); | |
gl.vertexAttribPointer(programInfo.attribLocations.textureCoord, num, type, normalize, stride, offset); | |
gl.enableVertexAttribArray(programInfo.attribLocations.textureCoord); | |
} | |
// Tell WebGL we want to affect texture unit 0 | |
gl.activeTexture(gl.TEXTURE0); | |
// Bind the texture to texture unit 0 | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
// Tell the shader we bound the texture to texture unit 0 | |
gl.uniform1i(programInfo.uniformLocations.sampler, 0); | |
// Tell WebGL which indices to use to index the vertices | |
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices); | |
{ | |
const vertexCount = 6; | |
const type = gl.UNSIGNED_SHORT; | |
const offset = 0; | |
gl.drawElements(gl.TRIANGLES, vertexCount, type, offset); | |
} | |
} | |
function drawParticles(gl, programInfo, buffers, texture, object_physics){ | |
// Tell WebGL to use our program when drawing | |
gl.useProgram(programInfo.program.instanceProgram); | |
// typical instantiation | |
{ | |
const numComponents = 2; // pull out 2 values per iteration | |
const type = gl.FLOAT; // the data in the buffer is 32bit floats | |
const normalize = false; // don't normalize | |
const stride = 0; // how many bytes to get from one set of values to the next | |
// 0 = use type and numComponents above | |
const offset = 0; // how many bytes inside the buffer to start from | |
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); | |
gl.vertexAttribPointer( | |
programInfo.attribLocations.instancePosition, | |
numComponents, | |
type, | |
normalize, | |
stride, | |
offset); | |
gl.enableVertexAttribArray( | |
programInfo.attribLocations.instancePosition); | |
} | |
// tell webgl how to pull out the texture coordinates from buffer | |
{ | |
const num = 2; // every coordinate composed of 2 values | |
const type = gl.FLOAT; // the data in the buffer is 32 bit float | |
const normalize = false; // don't normalize | |
const stride = 0; // how many bytes to get from one set to the next | |
const offset = 0; // how many bytes inside the buffer to start from | |
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.textureCoord); | |
gl.vertexAttribPointer(programInfo.attribLocations.instanceCoord, num, type, normalize, stride, offset); | |
gl.enableVertexAttribArray(programInfo.attribLocations.instanceCoord); | |
} | |
// Tell WebGL we want to affect texture unit 0 | |
gl.activeTexture(gl.TEXTURE0); | |
// Bind the texture to texture unit 0 | |
gl.bindTexture(gl.TEXTURE_2D, texture); | |
// Tell the shader we bound the texture to texture unit 0 | |
gl.uniform1i(programInfo.uniformLocations.instanceSampler, 0); | |
// Tell WebGL which indices to use to index the vertices | |
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices); | |
// position the instances | |
object_physics.forEach(function(particle_object , index) { | |
// scale it to a size | |
gl.uniform1f( | |
programInfo.uniformLocations.instanceScaling, | |
particle_object.scaling | |
); | |
// translate from the top left corner | |
gl.uniform4fv( | |
programInfo.uniformLocations.instanceTranslation, | |
particle_object.location | |
); | |
// rotation | |
const rotationMatrix = mat4.create(); | |
mat4.rotate(rotationMatrix, // destination matrix | |
rotationMatrix, // matrix to rotate | |
particle_object.rotation, // amount to rotate in radians | |
[0, 0, 1]); // axis to rotate around | |
gl.uniformMatrix4fv( | |
programInfo.uniformLocations.instanceRotation, | |
false, | |
rotationMatrix | |
); | |
{ | |
const vertexCount = 6; | |
const type = gl.UNSIGNED_SHORT; | |
const offset = 0; | |
gl.drawElements(gl.TRIANGLES, vertexCount, type, offset , ); | |
} | |
}); | |
// console.log(programInfo.uniformLocations.instanceScaling , programInfo.uniformLocations.instanceTranslation , programInfo.uniformLocations.instanceRotation , programInfo.attribLocations.instancePosition, programInfo.attribLocations.instanceCoord , programInfo.uniformLocations.instanceSampler); | |
// gl.uniform1f( | |
// programInfo.uniformLocations.instanceScaling, | |
// scales | |
// ); | |
// gl.uniformMatrix4fv( | |
// programInfo.uniformLocations.instanceTranslation, | |
// false, | |
// translations | |
// ); | |
// gl.uniformMatrix4fv( | |
// programInfo.uniformLocations.instanceRotation, | |
// false, | |
// rotations | |
// ); | |
} | |
function audiovisualFX(audio_obj) { | |
var frequency_profile = new Uint8Array(audio_obj.analyser.frequencyBinCount); | |
audio_obj.analyser.getByteFrequencyData(frequency_profile); | |
var bands = assessBandVolumes(frequency_profile); | |
return bands; | |
} | |
function assessBandVolumes(frequency_profile) { | |
var bands = [ | |
0, // "low low ": | |
0, // "low " | |
0, // "mid low ": | |
0, // "mid ": | |
0, // "mid high ": | |
0 // "high ": | |
]; | |
var len = frequency_profile.length; // add in a manual frequency cuttoff since certain ranges are empty | |
const DIVISIONS = 170; | |
var empty_point = 0; | |
for (var fq = 0; fq < len; fq++) { | |
bands[fq / DIVISIONS > 5 ? 5 : Math.floor(fq / DIVISIONS)] += | |
frequency_profile[fq]; | |
} | |
for (var band_no = 0; band_no < 6; band_no++) { | |
bands[band_no] = bands[band_no] / DIVISIONS / 256; | |
} | |
return bands; | |
} | |
init(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
1) basic image init | |
2) circlar partical effect that can be unraveled into a line | |
3) sync to audio | |
*/ | |
/* | |
1 ) render all vissibles to screen (background and 2 stars in different locations) | |
2) Design emitter for circle and band | |
3) On location click, emit with from random position with acceleration normal to surface and random rotation | |
*/ | |
/* | |
Concepts: | |
1) scaling and translation of items | |
2) glDrawElementsInstanced | |
3) Creating an emission surface of length and curvature angles (0 is flat, 360 is circle, 180 is half) | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment