【问题标题】:canvas to WebGL [closed]画布到 WebGL [关闭]
【发布时间】:2016-12-19 19:20:52
【问题描述】:

我尝试学习 WebGL,但我什么都没有。 我不想使用图书馆,它们太大了。 我在画布上写了我想要的,帮我在 WebGL 上做吧

https://jsfiddle.net/g9tx903c/

<canvas id="canvas" width="500" height="200"></canvas>

<script type='text/javascript'>
    function DrawPoint(x, y, size, blur, opacity) {
        var c = document.getElementById('canvas');
        var ctx = c.getContext('2d');

        var halfSize = size / 2;

        var radgrad = ctx.createRadialGradient(halfSize + x, halfSize + y, 0, halfSize + x, halfSize + y, halfSize);
        radgrad.addColorStop(blur, 'rgba(255, 255, 255, ' + opacity +')');
        radgrad.addColorStop(1, 'rgba(255, 255, 255, 0)');
        ctx.fillStyle = radgrad;
        ctx.fillRect(x, y, size, size);
    }


    function DrawcGradient() {
        var w = 500;
        var h = 200;

        var pointR = w / 2;
        var x = (w / 2);
        var y = (h / 2);

        var c = document.getElementById('canvas');
        var ctx = c.getContext('2d');
        var grd = ctx.createRadialGradient(x, y, 0, x, y, pointR);
        grd.addColorStop(0, '#5A6977');
        grd.addColorStop(1, '#000');
        ctx.fillStyle = grd;
        ctx.fillRect(0, 0, w, h);
    }


    DrawcGradient();
    DrawPoint(50, 100, 50, 0.5, 0.2);
    DrawPoint(70, 10, 150, 0.93, 0.2);
</script>

【问题讨论】:

  • 您可以开始使用HelloWorld 示例。
  • 为什么要使用WebGL?你的目标是什么?
  • 画布不能快速工作

标签: javascript html canvas webgl


【解决方案1】:

这恰好属于我正在追赶的领域。也许不是你想要的,但你可以删除所有 2D 画布的东西,只保留 webGL 的东西。

此演示混合了 2D 和 3D 画布界面,以获得对 2D 画布内容的高性能 GPU 处理。

片段着色器负责在名为FragmentShader的代码底部附近创建渐变

径向渐变是通过计算与中心的距离并获得一个 0 到 1 的值来创建的。我使用这个值来获得 3 个渐变。要更改我相乘的渐变的锐度,请下拉并限制值。

例如,鼠标附近的大圆圈是 1 在中心和 0 远。我乘以 350 得到 350 到 0。然后我下拉 270 得到 80 到 -270,然后我钳制到 0 和 1。结果是一个急剧的梯度。

我将圆圈从画布顶部的一点开始放置在一条线上,一个靠近鼠标,另一个沿着线更远。

有很多样板的东西,我今天有点过头了,所以你必须把剩下的弄清楚。

很容易转换为所有 webGL。只需删除顶部的所有画布鼠标。创建图像并调用startWebGL(image)webGL 画布(在startWebGL 函数中创建后)添加到文档正文并至少调用一次函数webGLRender 来设置着色器变量并渲染帧。

警告着色器有高级指令,没有支持代码就不能使用。要与其他设置一起使用,请删除着色器中所有 uniformattribute 变量前面的 #。即#uniform vec2 mouse;变成uniform vec2 mouse;

//==================================================================================================
// 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);

【讨论】:

  • 如果我需要生成很多球,我该怎么做?我要达到这个效果huaban.com/pins/604178757
  • @Fulrus 您将需要一个 webGL 2D 粒子特效,或者可以使用反馈纹理来完成,但会非常棘手,并且可能无法在所有机器上正常工作。 webGL 粒子将是最好的解决方案,甚至仅在 2D 画布中也可以正常工作。
猜你喜欢
  • 2013-01-05
  • 2016-01-27
  • 2017-10-16
  • 1970-01-01
  • 1970-01-01
  • 2019-03-20
  • 2014-11-07
  • 2019-05-07
  • 2013-10-02
相关资源
最近更新 更多