JavaScript
语言:
JaveScriptBabelCoffeeScript
确定
var CANVAS_WIDTH = 640;
var CANVAS_HEIGHT = 480;
var TIME_DIVIDER = 10000;
var NUMBER_OF_PARTICLES = 120000;
var SPAWN_MIN = 0.;
var SPAWN_IN_CIRCLE = false;
var BLEND_ADDITIVE = true;
var DEPTH_TEST = false;
var BACKGROUND_COLOR = {
red: 0.1,
green: 0.1,
blue: 0.1
};
var gl; //WebGL context
var canvasElement;
var codeElement;
var editingVertexShader = true;
var pointBuffer; //array buffer for particle points
var timeUniform; //uniform location for Time
var shaderProgram, vertexShader, fragmentShader;
var startTime;
//on page load
window.onload = function() {
codeElement = document.getElementById("code");
canvasElement = document.getElementById("display");
canvasElement.width = CANVAS_WIDTH;
canvasElement.height = CANVAS_HEIGHT;
gl = canvasElement.getContext("webgl");
if (!gl) {
gl = canvasElement.getContext("experimental-webgl"); // if getting the context for 'webgl' fails try and get the experimental context
if (!gl) {
alert("WebGL is not supported in this browser!");
return;
}
}
//Particle vertex positions
var particleSpawnPoints = new Float32Array(2 * NUMBER_OF_PARTICLES);
//Iterate through particles spawning random positions from -1 to 1
var i = NUMBER_OF_PARTICLES;
if (SPAWN_IN_CIRCLE) {
while (i--) {
var spawnAng = Math.random() * Math.PI * 2.;
var spawnDist = Math.random();
while (spawnDist < SPAWN_MIN) spawnDist = Math.random();
var particleIndex = i * 2;
particleSpawnPoints[particleIndex++] = Math.sin(spawnAng) * spawnDist; //Put generated spawn position in flow array
particleSpawnPoints[particleIndex] = Math.cos(spawnAng) * spawnDist;
}
} else {
while (i--) {
var spawnX = Math.random() * 2 - 1;
while (spawnX * spawnX < SPAWN_MIN) spawnX = Math.random() * 2 - 1;
var spawnY = Math.random() * 2 - 1;
while (spawnY * spawnY < SPAWN_MIN) spawnY = Math.random() * 2 - 1;
var particleIndex = i * 2;
particleSpawnPoints[particleIndex++] = spawnX; //Put generated spawn position in flow array
particleSpawnPoints[particleIndex] = spawnY;
}
}
//Create array buffer
pointBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, pointBuffer);
gl.bufferData(gl.ARRAY_BUFFER, particleSpawnPoints, gl.STATIC_DRAW); //Move float array of spawn points into array buffer
ReloadShader(true);
gl.enable(gl.BLEND);
if (BLEND_ADDITIVE) {
gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
} else {
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
}
if (DEPTH_TEST) {
gl.enable(gl.DEPTH_TEST);
} else {
gl.disable(gl.DEPTH_TEST);
}
gl.clearColor(BACKGROUND_COLOR.red, BACKGROUND_COLOR.green, BACKGROUND_COLOR.blue, 1);
startTime = Date.now();
render();
};
function render() {
requestAnimationFrame(render);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.uniform1f(timeUniform, (Date.now() - startTime) / TIME_DIVIDER);
gl.drawArrays(gl.POINTS, 0, NUMBER_OF_PARTICLES);
}
function SaveChanges() {
if (editingVertexShader) {
window.localStorage.vertexCode = codeElement.value;
} else {
window.localStorage.fragmentCode = codeElement.value;
}
}
function ReloadShader(first) {
if (!first) {
//Free shaders
gl.deleteProgram(shaderProgram);
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
SaveChanges();
} else {
//Set default vertex/fragment shader code if there isn't any saved code
if (!window.localStorage.vertexCode) {
window.localStorage.vertexCode = ENCIRCLINGSYSTEM_VERTEX;
}
if (!window.localStorage.fragmentCode) {
window.localStorage.fragmentCode = ENCIRCLINGSYSTEM_FRAGMENT;
}
if (editingVertexShader) {
codeElement.value = window.localStorage.vertexCode;
} else {
codeElement.value = window.localStorage.fragmentCode;
}
}
vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, window.localStorage.vertexCode);
gl.compileShader(vertexShader);
if (!debugShader(vertexShader)) alert("Error in vertex shader! Check the developer console of your browser for a detailed error description.");
fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, window.localStorage.fragmentCode);
gl.compileShader(fragmentShader);
if (!debugShader(fragmentShader)) alert("Error in fragment shader! Check the developer console of your browser for a detailed error description.");
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
var vertAttrib = gl.getAttribLocation(shaderProgram, "Vertex");
gl.enableVertexAttribArray(vertAttrib);
gl.vertexAttribPointer(pointBuffer, 2, gl.FLOAT, false, 4, 0);
timeUniform = gl.getUniformLocation(shaderProgram, "Time");
}
function debugShader(shader) {
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.log(gl.getShaderInfoLog(shader));
return false;
}
return true;
}
function SwitchShaderCode() {
SaveChanges();
codeElement.value = editingVertexShader ? window.localStorage.fragmentCode : window.localStorage.vertexCode;
document.getElementById("switchBtn").value = editingVertexShader ? "Switch to vertex shader" : "Switch to fragment shader";
editingVertexShader = !editingVertexShader;
}
function SimplestPreset() {
window.localStorage.vertexCode = SIMPLESYSTEM_VERTEX;
window.localStorage.fragmentCode = SIMPLESYSTEM_FRAGMENT;
codeElement.value = !editingVertexShader ? window.localStorage.fragmentCode : window.localStorage.vertexCode;
ReloadShader(false);
}
function EncirclingPreset() {
window.localStorage.vertexCode = ENCIRCLINGSYSTEM_VERTEX;
window.localStorage.fragmentCode = ENCIRCLINGSYSTEM_FRAGMENT;
codeElement.value = !editingVertexShader ? window.localStorage.fragmentCode : window.localStorage.vertexCode;
ReloadShader(false);
}
function FlagPreset() {
window.localStorage.vertexCode = FLAGSYSTEM_VERTEX;
window.localStorage.fragmentCode = FLAGSYSTEM_FRAGMENT;
codeElement.value = !editingVertexShader ? window.localStorage.fragmentCode : window.localStorage.vertexCode;
ReloadShader(false);
}
function Export() {
var qt = '"';
var displayId = "psysdisplayed" + Math.floor(Math.random() * 100000);
var styleStr = "html, body {color: white; text-align: center; background-color: rgb(" + Math.floor(BACKGROUND_COLOR.red * 255) + "," + Math.floor(BACKGROUND_COLOR.green * 255) + "," + Math.floor(BACKGROUND_COLOR.blue * 255) + ")}";
var headStr = "Made using Ethan Shulmans particle vertex/fragment shader editor";
var htmlStr = "var width = " + CANVAS_WIDTH + "; var height = " + CANVAS_HEIGHT + "; var vbuf; var pstart; var nump; var canvasElement; var gl; window.onload = function() { canvasElement = document.getElementById(" + qt + displayId + qt + "); canvasElement.width = width; canvasElement.height = height; gl = canvasElement.getContext(" + qt + "webgl" + qt + "); if (!gl) { gl = canvasElement.getContext(" + qt + "experimental-webgl" + qt + "); if (!gl) { alert(" + qt + "WebGL not supported!" + qt + ");}} nump = " + NUMBER_OF_PARTICLES + "; pstart = new Float32Array(nump*2); var i = nump; while (i--) {var ai =i*2;";
if (SPAWN_IN_CIRCLE) {
htmlStr += "var spawnAng = Math.random()*Math.PI*2.; var spawnDist = Math.random(); while (spawnDist < " + SPAWN_MIN + ") {spawnDist = Math.random();} var ai = i*2; pstart[ai++] = Math.sin(spawnAng)*spawnDist; pstart[ai] = Math.cos(spawnAng)*spawnDist; }";
} else {
htmlStr += "var ai = i*2; var bf = Math.random(); while (bf*bf < " + SPAWN_MIN + ") {bf = Math.random();} pstart[ai++] = bf; bf = Math.random(); while(bf*bf < " + SPAWN_MIN + ") {bf = Math.random();} pstart[ai] = bf; }";
}
var vertPlaced = window.localStorage.vertexCode.replace(/(\r\n|\n|\r)/gm, " ");
var fragPlaces = window.localStorage.fragmentCode.replace(/(\r\n|\n|\r)/gm, " ");
htmlStr += "vbuf = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,vbuf); gl.bufferData(gl.ARRAY_BUFFER,pstart,gl.STATIC_DRAW); var vsh = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vsh, " + qt + vertPlaced + qt + "); gl.compileShader(vsh); var fsh = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fsh," + qt + fragPlaces + qt + "); gl.compileShader(fsh); var program = gl.createProgram(); gl.attachShader(program,vsh); gl.attachShader(program,fsh); gl.linkProgram(program); gl.useProgram(program); var vattr = gl.getAttribLocation(program," + qt + "Vertex" + qt + "); gl.enableVertexAttribArray(vattr); gl.vertexAttribPointer(vbuf,2,gl.FLOAT,false,4,0); tuni = gl.getUniformLocation(program," + qt + "Time" + qt + "); gl.enable(gl.BLEND);";
if (BLEND_ADDITIVE) {
htmlStr += "gl.blendFunc(gl.SRC_ALPHA,gl.ONE);";
} else {
htmlStr += "gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);";
}
if (DEPTH_TEST) {
htmlStr += " gl.enable(gl.DEPTH_TEST);";
} else {
htmlStr += "gl.disable(gl.DEPTH_TEST);";
}
htmlStr += "gl.clearColor(" + BACKGROUND_COLOR.red + "," + BACKGROUND_COLOR.green + "," + BACKGROUND_COLOR.blue + ",1); startTime = Date.now(); render(); }; var tuni,startTime; function render() { gl.clear(gl.COLOR_BUFFER_BIT); gl.uniform1f(tuni,(Date.now()-startTime)/" + TIME_DIVIDER + "); gl.drawArrays(gl.POINTS,0,nump); window.requestAnimationFrame(render); }";
var myWindow = window.open("", "Export Window", "width=400, height=300");
myWindow.document.body.appendChild(document.createTextNode("HTML = " + headStr));
myWindow.document.body.appendChild(document.createElement("hr"));
myWindow.document.body.appendChild(document.createTextNode("CSS = " + styleStr));
myWindow.document.body.appendChild(document.createElement("hr"));
myWindow.document.body.appendChild(document.createTextNode("JAVASCRIPT = "));
myWindow.document.body.appendChild(document.createTextNode(htmlStr));
}
var SIMPLESYSTEM_VERTEX = "precision mediump float;\n\n" +
"attribute vec2 Vertex;\n" +
"uniform float Time;\n\n" +
"void main() {\n" +
"gl_PointSize = 1.;\n" +
"gl_Position = vec4(Vertex*mod(Time,1.),0.,1.);\n" +
"}";
var SIMPLESYSTEM_FRAGMENT = "precision mediump float;\n\n" +
"void main() {\n" +
"gl_FragColor = vec4(1.,1.,1.,.1);\n" +
"}";
var ENCIRCLINGSYSTEM_VERTEX = "precision mediump float;\n\n" +
"attribute vec2 Vertex;\nuniform float Time;\n" +
"void main() {\n" +
"gl_PointSize = 2.5;\n\n" +
"vec2 v = (Vertex*.4- Vertex*(mod(Time+length(Vertex),1.)))*vec2(cos(Time),1.);\n" +
"float ct = (cos(v.x*30.+Time*5.)+cos(v.y*30.+Time*20.));\n" +
"v = mat2(cos(v.x*(1.+ct)),-sin(v.x*(1.+ct)),sin(v.y*(32.+ct)),cos(v.y*(32.+ct)))*v;\n\n" +
"gl_Position=vec4(v,0.,1.);\n" +
"}";
var ENCIRCLINGSYSTEM_FRAGMENT = "\nprecision mediump float;\n\n" +
"void main() {\n" +
"gl_FragColor = vec4(1.,.6,.6,.1);\n}";
var FLAGSYSTEM_VERTEX = "\nprecision mediump float;\n\n" +
"attribute vec2 Vertex;\n varying vec2 V;\n uniform float Time;\n\n" +
"void main(void) {\n" +
"gl_PointSize = 2.;\nV = Vertex;\n\n float pt = (mod(Time+length(Vertex)*.1+.9,.65));\n float ang = atan(Vertex.y,Vertex.x);\n" +
"vec2 v = (Vertex+vec2(sin(ang),cos(ang))*pt)*pt*(cos(ang*30.)*.5*max(0.,pt-.2)*16.+1.);\n" +
"float ct = (pt*4.);\n" +
"v = mat2(sin(ct),cos(ct),cos(ct),-sin(ct))*v;\n\n" +
"gl_Position=vec4(v,0.,1.);\n" +
"}";
var FLAGSYSTEM_FRAGMENT = "\nprecision mediump float;\n\n varying vec2 V;\n uniform float Time;\n\n" +
"void main(void) {\n\n" +
"gl_FragColor = mix(mix(vec4(1.,1.,1.,.05),vec4(1.,0.,0.,.05),floor(mod(V.y+1.175,.325)*6.)),vec4(.2,.2,1.,.05),clamp(1.-length(max(abs(V+vec2(.7,.7))-vec2(.3),0.))/.01, 0., 1.));\n\n}";