//==================================================================================================
// The following code is support code that provides me with a standard interface to various forums.
// It provides a mouse interface, a full screen canvas, and some global often used variable
// like canvas, ctx, mouse, w, h (width and height), globalTime
// It should not be used as an example of how to write a canvas interface.
// By Blindman67
const U = undefined;
const RESIZE_DEBOUNCE_TIME = 100;
var onresize; // demo use this to do your thing
var w,h,cw,ch,canvas,ctx,mouse,createCanvas,resizeCanvas,setGlobals,globalTime=0,resizeCount = 0;
createCanvas = function() { // create 2D display canvas
var c,cs;
cs = (c = document.createElement("canvas")).style;
cs.position = "absolute";
cs.top = cs.left = "0px";
cs.zIndex = 1000;
document.body.appendChild(c);
return c;
}
resizeCanvas = function() {
if (canvas === U) {
canvas = createCanvas();
}
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") {
setGlobals();
}
if (typeof onresize === "function"){
resizeCount += 1;
setTimeout(debounceResize,RESIZE_DEBOUNCE_TIME);
}
}
function debounceResize(){
resizeCount -= 1;
if(resizeCount <= 0){
onresize();
}
}
setGlobals = function(){
cw = (w = canvas.width)/2;
ch = (h = canvas.height)/2;
mouse.updateBounds();
}
mouse = (function(){
function preventDefault(e) { e.preventDefault(); }
var mouse = {
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false, buttonRaw : 0, over : false, bm : [1, 2, 4, 6, 5, 3],
active : false,bounds : null, crashRecover : null, mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
var m = mouse;
function mouseMove(e) {
var t = e.type;
m.x = e.clientX - m.bounds.left; m.y = e.clientY - m.bounds.top;
m.alt = e.altKey; m.shift = e.shiftKey; m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1]; }
else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2]; }
else if (t === "mouseout") { m.buttonRaw = 0; m.over = false; }
else if (t === "mouseover") { m.over = true; }
else if (t === "mousewheel") { m.w = e.wheelDelta; }
else if (t === "DOMMouseScroll") { m.w = -e.detail; }
if (m.callbacks) { m.callbacks.forEach(c => c(e)); }
if((m.buttonRaw & 2) && m.crashRecover !== null){ if(typeof m.crashRecover === "function"){ setTimeout(m.crashRecover,0);}}
e.preventDefault();
}
m.updateBounds = function(){
if(m.active){
m.bounds = m.element.getBoundingClientRect();
}
}
m.addCallback = function (callback) {
if (typeof callback === "function") {
if (m.callbacks === U) { m.callbacks = [callback]; }
else { m.callbacks.push(callback); }
} else { throw new TypeError("mouse.addCallback argument must be a function"); }
}
m.start = function (element, blockContextMenu) {
if (m.element !== U) { m.removeMouse(); }
m.element = element === U ? document : element;
m.blockContextMenu = blockContextMenu === U ? false : blockContextMenu;
m.mouseEvents.forEach(n => { m.element.addEventListener(n, mouseMove); });
if (m.blockContextMenu === true) { m.element.addEventListener("contextmenu", preventDefault, false); }
m.active = true;
m.updateBounds();
}
m.remove = function() {
if (m.element !== U) {
m.mouseEvents.forEach(n => { m.element.removeEventListener(n, mouseMove); });
if (m.contextMenuBlocked === true) { m.element.removeEventListener("contextmenu", preventDefault);}
m.element = m.callbacks = m.contextMenuBlocked = U;
m.active = false;
}
}
return mouse;
})();
/** SimpleFullCanvasMouse.js end **/
function display(){
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.clearRect(0,0,w,h);
if(webGL !== undefined){
webGLRender();
}
}
function update(timer){ // Main update loop
globalTime = timer;
display(); // call demo code
requestAnimationFrame(update);
}
// END of boilerplate stuff.
/* ***************************************************************************************************
The following functions are helpers for Shader variables. Rather than having to type all the mumbo
jumbo to locate shader variable and then store the location ID getVariables and getLocations does
it for you. uniform and attribute variable that are prefixed with # are can used via the gl.locs.name
(no space between # and variable type #attribute is good # attribute is bad)
For example
#uniform vec3 myVec; // the shader source code
Is located and given the name myVec in gl.locs and can be set in javascript
gl.uniform3f(gl.locs.myVec, 0.3, 0.5, 0.8); //
Please not that this makes shaders source code none standard and will not complie as is without these
functions . Just remove the #
*************************************************************************************************** */
const VAR_TYPES = ["attribute","uniform"];
const VAR_LOCATE_FUNC = {attribute : "getAttribLocation", uniform : "getUniformLocation"}
// get # delimited variables from shader source
function getVariables(script,types){
VAR_TYPES.forEach(f => {
if(types.items === undefined){ types.items = []; }
script = script.replace(new RegExp("#" + f+".+;","g"), str => {
var data = str.replace(/ /g," ").split(" ");
types.items.push({use : f , type : data[1] , name : data[2].replace(";","")});
return str.substr(1);
})
})
return script;
}
// get location IDs for shader variables
var getLocations = function(gl,shaders){
var locs = {};
shaders.variables.items.forEach(v => { locs[v.name] = gl[VAR_LOCATE_FUNC[v.use]](shaders.program, v.name); });
return locs;
}
/* end of var heplers ***************************************************************************** */
// creates vertex and fragment shaders
function createProgramFromScripts(gl, ids) {
var shaders = [];
var variables = {};
for (var i = 0; i < ids.length; i += 1) {
var script = shadersSource[ids[i]];
if (script !== undefined) {
var shader = gl.createShader(gl[script.type]);
var source = getVariables(script.source,variables)
gl.shaderSource(shader, source);
gl.compileShader(shader);
shaders.push(shader);
}else{
throw new ReferenceError("*** Error: unknown script ID : " + ids[i]);
}
}
var program = gl.createProgram();
shaders.forEach((shader) => { gl.attachShader(program, shader); });
gl.linkProgram(program);
gl.locs = getLocations(gl,{ program : program,variables : variables});
return program;
}
// setup simple 2D webGL image processor
var webGL;
function startWebGL(image) {
webGL = document.createElement("canvas");
webGL.width = image.width;
webGL.height = image.height;
var gl = webGL.gl = webGL.getContext("webgl");
var program = createProgramFromScripts(gl, ["VertexShader", "FragmentShader"]);
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(gl.locs.texCoord);
gl.vertexAttribPointer(gl.locs.texCoord, 2, gl.FLOAT, false, 0, 0);
gl.bindTexture(gl.TEXTURE_2D, gl.createTexture());
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.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.uniform2f(gl.locs.resolution, webGL.width, webGL.height);
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.enableVertexAttribArray(gl.locs.position);
gl.vertexAttribPointer(gl.locs.position, 2, gl.FLOAT, false, 0, 0);
setRectangle(gl, 0, 0, image.width, image.height);
}
function setRectangle(gl, x, y, width, height) { // set draw rectangle
var x1 = x + width;
var y1 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ x, y, x1, y, x, y1, x, y1, x1, y, x1, y1]), gl.STATIC_DRAW);
}
var shadersSource = {
VertexShader : {
type : "VERTEX_SHADER",
source : `
// the # is a higher level directive to indicate that the variable needs to
// be loacted as value set or read
#attribute vec2 position;
#attribute vec2 texCoord;
#uniform vec2 resolution;
varying vec2 u_texCoord; // varying means this is moved to the frag shader
float aspect = resolution.x/resolution.y;
varying float aspect1;
void main() {
vec2 zeroToOne = position/resolution;
vec2 zeroToTwo = zeroToOne * 2.0;
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
u_texCoord = vec2(texCoord.x ,texCoord.y/aspect);
aspect1 = aspect;
}`
},
FragmentShader : {
type : "FRAGMENT_SHADER",
source : `
// the # is a higher level directive to indicate that the variable needs to
// be loacted as value set or read
precision mediump float;
#uniform sampler2D u_image;
#uniform vec4 backDark; // the background dark colour
#uniform vec4 backLight; // backgroun light colour
#uniform vec4 ringCol; // rings colour
#uniform vec2 lightLoc; // location of point from which to project second ring
#uniform vec2 mouse; // location of big ring
varying float aspect1;
varying vec2 u_texCoord; // texture coord for all mapping and comes from the
// vertext shader. If you change the name here
// change it in the vert shader
float dist; // used for background gradient
vec4 pixelCol; // to hold the final pixel colour
vec2 gradCenter = vec2(0.5,0.25); // location of background gradient center
vec2 secondRing = lightLoc+ (mouse - lightLoc) * (1.2 + distance(mouse,lightLoc));
void main() {
pixelCol = texture2D(u_image, vec2(u_texCoord.x,u_texCoord.y * aspect1));
//pixelCol = texture2D(u_image, vec2(u_texCoord.x,u.texCoord.y);
// get distance from center of background gradient
dist = distance(gradCenter,u_texCoord)/0.707;
// use dist to caculate the background gradient
pixelCol += (backDark - backLight) * dist + backLight;
// add the big ring colour to the background. mouse is the center of big ring
pixelCol += clamp((1.0-distance(mouse,u_texCoord)) * 345.0 - 270.5 ,0.0,1.0) * ringCol;
// add the second rign colour to the background colour. secondRing is location of second ring
pixelCol += clamp((1.0-distance(secondRing,u_texCoord)) * 29.0 - 27. ,0.0,1.0) * ringCol;
gl_FragColor = pixelCol; // set the fragment to the colour caculated
}`
}
}
var firstRun = true;
function webGLRender(){
var gl = webGL.gl;
if(firstRun){
firstRun = false;
gl.uniform4f(gl.locs.backDark, 0,0,0,1)
gl.uniform4f(gl.locs.backLight, 0.3,0.5,0.8,1)
gl.uniform4f(gl.locs.ringCol, 0.1,0.1,0.38,0.41)
gl.uniform2f(gl.locs.lightLoc, 0.5,0.0);
}
gl.uniform2f(gl.locs.mouse, mouse.x/w,mouse.y/h);
gl.drawArrays(gl.TRIANGLES, 0, 6);
ctx.drawImage(webGL,0,0, canvas.width, canvas.height);
}
function createImageAndStartWebGL(){
var image = document.createElement("canvas");
image.width = canvas.width;
image.height = canvas.height;
image.ctx = image.getContext("2d");
image.ctx.fillRect(0,0,canvas.width,canvas.height);
image.ctx.font = "48px arial";
image.ctx.textAlign = "center";
image.ctx.fillStyle = "white";
image.ctx.fillText("WebGL & Canvas 2D demo",canvas.width/2,48);
image.ctx.font = "16px arial";
image.ctx.fillText("WebGL fragment shader processing 2D canvas image, then render back to 2D canvas.",canvas.width/2,66);
firstRun = true;
startWebGL(image);
}
// Add the setup to the debounced resize
onresize = createImageAndStartWebGL;
// start it all happening
resizeCanvas();
mouse.start(canvas,true);
window.addEventListener("resize",resizeCanvas);
requestAnimationFrame(update);
你可以開始使用[的HelloWorld(http://webglfundamentals.org/webgl/lessons/webgl-fundamentals.html)的例子。 –
你爲什麼要使用WebGL?你的目標是什麼? – LarsH
帆布不會很快 – Fulrus