2016-07-12 96 views
0

我是WebGL編程的初學者。WebGL:平滑淡出線條畫布

我在Three.JS中製作了一個Web應用程序,它在畫布上繪製了一個偶爾有噪音的正弦波。在他們被吸引之後,我淡化了他們。最終的效果看起來是這樣的:

enter image description here

我試圖使因爲與three.js所的速度問題WebGL的應用。我能夠在WebGL中繪製出一個簡單的正弦波,但不知道如何達到相同的效果,我可以繪製單個波形,以某種方式將其保存在緩衝區中,並將其淡入淡出。

這是我目前有(在WebGL的):

enter image description here

而且,這裏是有關的代碼:

this.gl; 
    try { 
     this.gl = this.canvas.getContext('experimental-webgl',{antialias: false}); 
    } catch (e) { 
     alert('WebGL not supported.'); 
    } 

    //set position of vertices in clip coordinates 
    this.vtxShaderSrc = "\n\ 
    attribute vec2 position;\n\ 
    uniform vec2 viewport;\n\ 
    \n\ 
    void main(void){\n\ 
     \n\ 
     gl_Position = vec4((position/viewport)*2.0-1.0, 0.0, 1.0);\n\ 
    }"; 
    //fragment shader returns the color of pixel 
    this.fmtShaderSrc = "\n\ 
    precision mediump float;\n\ 
    \n\ 
    \n\ 
    \n\ 
    void main(void){\n\ 
     int r = 255;\n\ 
     int g = 255;\n\ 
     int b = 255;\n\ 
     gl_FragColor = vec4(r/255,g/255,b/255,1.);\n\ 
    }"; 

    this.getShader = function(source, type){ 
     var shader = this.gl.createShader(type); 
     this.gl.shaderSource(shader, source); 
     this.gl.compileShader(shader); 
     return shader; 
    } 

    this.vtxShader = this.getShader(this.vtxShaderSrc, this.gl.VERTEX_SHADER); 
    this.fmtShader = this.getShader(this.fmtShaderSrc, this.gl.FRAGMENT_SHADER); 

    this.program = this.gl.createProgram(); 
    //attach fragment and vertex shader to program 
    this.gl.attachShader(this.program, this.vtxShader); 
    this.gl.attachShader(this.program, this.fmtShader); 
    //link program to WebGL 
    this.gl.linkProgram(this.program); 
    //get position attribute and enable it in vertex shader 
    this._position = this.gl.getAttribLocation(this.program, 'position'); 
    this.gl.enableVertexAttribArray(this._position); 
    //tell WebGL to use this program 
    this.gl.useProgram(this.program); 
    //create buffers 
    this.vertexBuffer = this.gl.createBuffer(); 
    this.facesBuffer = this.gl.createBuffer(); 

    this.lineVertices = []; 
    this.faceCount = []; 
    //bind them to WebGL 
    this.bindVertexBuffer = function(){ 
     this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer); 
     this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.lineVertices), this.gl.STREAM_DRAW); 
    } 
    this.bindFacesBuffer = function(){ 
     this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.facesBuffer); 
     this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.faceCount), this.gl.STREAM_DRAW);  
    } 
    this.bindVertexBuffer(); 
    this.bindFacesBuffer(); 
    //set background color to black 
    this.gl.clearColor(0.0,0.0,0.0,1.0); 
    //draw on canvas 



    this.draw = function(){ 
     this.gl.enable(this.gl.BLEND); 


     this.gl.viewport(0, 0, this.canvas.width, this.canvas.height); 
     this.gl.clear(this.gl.COLOR_BUFFER_BIT); 

     this.gl.vertexAttribPointer(this._position, 2, this.gl.FLOAT, false, 8*2, 0); 

     var loc = this.gl.getUniformLocation(this.program, 'viewport'); 
     this.gl.uniform2f(loc, this.canvas.width, this.canvas.height); 

     //draw only if number of lines is greater than 0 
     if(this.faceCount.length > 0){ 
      this.gl.drawElements(this.gl.LINE_STRIP, this.faceCount.length/4, this.gl.UNSIGNED_SHORT, 0); 
     } 


     this.gl.disable(this.gl.BLEND); 
    } 

    //update vertices and faces so next call to this.draw() updates the wave 
    this.update = function(newPts){ 
     this.lineVertices = newPts; 
     this.bindVertexBuffer(); 
     var faces = []; 
     for(var i = 0; i < this.lineVertices.length; i++) faces.push(i); 
     this.faceCount = faces; 
     this.bindFacesBuffer(); 
    } 

任何幫助/指針讚賞。謝謝

+0

有趣。如果我可能會問,那麼three.js實現的瓶頸是什麼? – user01

+0

@ user01我用於渲染three.js中的線條的方法會繪製額外的線條,並在繪製更多線條時應用較低的不透明度。它適用於一個甚至兩個波浪(例如sin和cos波)。不過,我計劃在一個畫布上疊加許多更多的波浪。隨着入站點數量增加到數萬個,這開始大幅降低FPS。直接處理WebGL顯然會將此性能提高20-50倍。我試圖模仿這個人使用的渲染方法:http://codeflow.org/entries/2013/feb/04/high-performance-js-heatmaps/ –

+0

跟蹤亮度變量或類似的東西。它應該從1.0開始,然後隨着時間的推移將其降爲0。將其發送到GPU並將gl_FragColor乘以亮度。它也可以將亮度降低完全移到GPU上,但我不確定這是否是您要查找的內容。 – user80667

回答

0

很難給出答案,因爲有an infinite number of ways to do it,但基本上WebGL只是一個rasteration API,所以如果你想讓某些東西淡出加班時間,你必須隨時渲染它,畫出你想淡出的東西更透明。

在僞代碼

for each thing to draw 
    compute its age 
    draw more transparent the older it is 
    (optionally, delete it if it's too old) 

這裏有一個畫布2D版本,以保持它的簡單

var ctx = document.querySelector("canvas").getContext("2d") 
 
var currentTime = 0; // in seconds 
 
var ageLimit = 1; // 1 second 
 
var birthDuration = 0.2; // 0.2 seconds 
 
var birthTimer = 0; 
 
var thingsToDraw = []; 
 

 
function addThingToDraw() { 
 
    thingsToDraw.push({ 
 
    timeOfBirth: currentTime, 
 
    x: Math.random(), 
 
    y: Math.random(), 
 
    }); 
 
} 
 

 
function computeAge(thing) { 
 
    return currentTime - thing.timeOfBirth; 
 
} 
 

 
function removeOldThings() { 
 
    while(thingsToDraw.length > 0) { 
 
    var age = computeAge(thingsToDraw[0]); 
 
    if (age < ageLimit) { 
 
     break; 
 
    } 
 
    // remove thing that's too old 
 
    thingsToDraw.shift(); 
 
    } 
 
} 
 

 
function drawThings() { 
 
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 
 
    
 
    thingsToDraw.forEach(function(thing) { 
 
    var age = computeAge(thing); 
 
    var lerp = age/ageLimit; 
 
    var x = ctx.canvas.width * thing.x; 
 
    var y = ctx.canvas.height * thing.y; 
 
    var radius = 10 + lerp * 10; // 10 to 20 
 
    var color = makeCSSRGBAColor(0, 0, 0, 1. - lerp); 
 
    drawCircle(ctx, x, y, radius, color); 
 
    }); 
 
} 
 

 
function drawCircle(ctx, x, y, radius, color) { 
 
    ctx.fillStyle = color; 
 
    ctx.beginPath(); 
 
    ctx.arc(x, y, radius, 0, Math.PI * 2, false); 
 
    ctx.fill(); 
 
} 
 

 
function makeCSSRGBAColor(r, g, b, a) { 
 
    return "rgba(" + r + "," + g + "," + b + "," + a + ")"; 
 
} 
 

 
var then = 0; 
 
function process(time) { 
 
    currentTime = time * 0.001; 
 
    var deltaTime = currentTime - then; 
 
    then = currentTime; 
 
    
 
    birthTimer -= deltaTime; 
 
    if (birthTimer <= 0) { 
 
    addThingToDraw(); 
 
    birthTimer = birthDuration; 
 
    } 
 
    
 
    removeOldThings(); 
 
    drawThings(); 
 
    
 
    requestAnimationFrame(process); 
 
} 
 
requestAnimationFrame(process);
canvas { border: 1px solid black; }
<canvas></canvas>

WebGL是沒有什麼不同,只是gl.cleardrawCircle一些替代ctx.clearRect函數繪製一個圓圈。

這是同樣的程序WebGL的版本

var gl = document.querySelector("canvas").getContext("webgl") 
 
var currentTime = 0; // in seconds 
 
var ageLimit = 1; // 1 second 
 
var birthDuration = 0.2; // 0.2 seconds 
 
var birthTimer = 0; 
 
var thingsToDraw = []; 
 

 
var vs = ` 
 
attribute vec4 position; 
 

 
uniform mat4 u_matrix; 
 

 
void main() { 
 
    gl_Position = u_matrix * position; 
 
} 
 
`; 
 

 
var fs = ` 
 
precision mediump float; 
 

 
uniform vec4 u_color; 
 

 
void main() { 
 
    gl_FragColor = u_color; 
 
} 
 
`; 
 

 
var program = twgl.createProgramFromSources(gl, [vs, fs]); 
 
var positionLocation = gl.getAttribLocation(program, "position"); 
 
var colorLocation = gl.getUniformLocation(program, "u_color"); 
 
var matrixLocation = gl.getUniformLocation(program, "u_matrix"); 
 

 
// make a circle of triangles 
 
var numAround = 60; 
 
var verts = []; 
 
for (var i = 0; i < numAround; ++i) { 
 
    addPoint(verts, i/numAround, 1); 
 
    addPoint(verts, (i + 1)/numAround, 1); 
 
    addPoint(verts, i/numAround, 0); 
 
} 
 
var numVerts = verts.length/2; 
 
var buf = gl.createBuffer(); 
 
gl.bindBuffer(gl.ARRAY_BUFFER, buf); 
 
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW); 
 

 

 
function addPoint(verts, angleZeroToOne, radius) { 
 
    var angle = angleZeroToOne * Math.PI * 2; 
 
    verts.push(Math.cos(angle) * radius, Math.sin(angle) * radius); 
 
} 
 

 
function addThingToDraw() { 
 
    thingsToDraw.push({ 
 
    timeOfBirth: currentTime, 
 
    x: Math.random(), 
 
    y: Math.random(), 
 
    }); 
 
} 
 

 
function computeAge(thing) { 
 
    return currentTime - thing.timeOfBirth; 
 
} 
 

 
function removeOldThings() { 
 
    while(thingsToDraw.length > 0) { 
 
    var age = computeAge(thingsToDraw[0]); 
 
    if (age < ageLimit) { 
 
     break; 
 
    } 
 
    // remove thing that's too old 
 
    thingsToDraw.shift(); 
 
    } 
 
} 
 

 
function drawThings() { 
 
    gl.clear(gl.CLEAR_BUFFER_BIT); 
 
    
 
    thingsToDraw.forEach(function(thing) { 
 
    var age = computeAge(thing); 
 
    var lerp = age/ageLimit; 
 
    var x = gl.canvas.width * thing.x; 
 
    var y = gl.canvas.height * thing.y; 
 
    var radius = 10 + lerp * 10; // 10 to 20 
 
    var color = [0, 0, 0, 1 - lerp]; 
 
    drawCircle(gl, x, y, radius, color); 
 
    }); 
 
} 
 

 
function drawCircle(gl, x, y, radius, color) { 
 
    var aspect = gl.canvas.clientWidth/gl.canvas.clientHeight; 
 
    var matrix = [ 
 
    2/gl.canvas.width * radius, 0, 0, 0, 
 
    0, 2/gl.canvas.height * radius, 0, 0, 
 
    0, 0, 1, 0, 
 
    x/gl.canvas.width * 2 - 1, y/gl.canvas.height * 2 - 1, 0, 1, 
 
    ]; 
 

 
    gl.bindBuffer(gl.ARRAY_BUFFER, buf); 
 
    gl.enableVertexAttribArray(positionLocation); 
 
    gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); 
 

 
    
 
    gl.useProgram(program); 
 
    gl.uniformMatrix4fv(matrixLocation, false, matrix); 
 
    gl.uniform4fv(colorLocation, color); 
 
    gl.drawArrays(gl.TRIANGLES, 0, numVerts); 
 
} 
 

 
function ortho(width, height) { 
 
    return [ 
 
    2/(width), 0, 0, 0, 
 
    0, 2/(height), 0, 0, 
 
    0, 0, 1, 0, 0, 
 
    (width)/(-width), (height)/(-height), -1, 1, 
 
    ]; 
 
} 
 

 
    
 
var then = 0; 
 
function process(time) { 
 
    currentTime = time * 0.001; 
 
    var deltaTime = currentTime - then; 
 
    then = currentTime; 
 
    
 
    birthTimer -= deltaTime; 
 
    if (birthTimer <= 0) { 
 
    addThingToDraw(); 
 
    birthTimer = birthDuration; 
 
    } 
 
    
 
    removeOldThings(); 
 
    drawThings(); 
 
    
 
    requestAnimationFrame(process); 
 
} 
 
requestAnimationFrame(process);
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/twgl.min.js"></script> 
 
<canvas></canvas>

我不想包括矩陣庫,但you can read about matrices here因爲幾乎每個人都成問題從一個形狀/着色器畢業時到2你可能想要read this about drawing multiple things

+0

感謝這樣一個詳細的答案。我要在我的項目中嘗試並實現這一點。順便提一下,你提供的第一個鏈接是我正在尋找的確切效果。另外,在你的WebGL'process'函數中,'time'會自動傳入嗎?它似乎不適合我。 –

+0

''process'由'requestAnimationFrame'調用。 'reqeustAnimationFrame'將頁面加載到毫秒後的時間傳遞給它稱爲 – gman

+0

完美的函數。非常感謝! –