2016-07-12 2 views
0

Ich bin ein Anfänger bei WebGL-Programmierung.WebGL: glatt Linien aus Canvas verblassen

Ich habe eine Webanwendung in Three.JS gemacht, die mit gelegentlichem Rauschen eine Sinowelle auf eine Leinwand zeichnet. Nachdem sie gezeichnet wurden, verblasse ich sie. Der Endeffekt sieht ungefähr so ​​aus:

enter image description here

Ich versuche, die Anwendung in WebGL wegen Geschwindigkeitsprobleme mit Three.js zu machen. Ich bin in der Lage, eine einfache Sinuswelle in WebGL zu zeichnen, aber ich weiß nicht, wie ich den gleichen Effekt erzielen kann, bei dem ich eine einzelne Welle zeichnen, sie irgendwie im Puffer behalten und verblassen kann.

Dies ist, was ich zur Zeit (in WebGL) haben:

enter image description here

Auch hier ist der entsprechende Code:

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(); 
    } 

Jede Hilfe/Zeiger geschätzt werden. Danke

+0

Interessant. Was war der Engpass für die Implementierung von three.js, wenn ich fragen darf? – user01

+0

@ user01 Die Methode, die ich zum Rendern der Linien in three.js verwende, zeichnet zusätzliche Linien und wendet eine niedrigere Deckkraft an, wenn mehr Linien gezeichnet werden. Es funktioniert gut für eine, sogar zwei Wellen (sagen sin und cos wave). Ich habe jedoch vor, viel mehr Wellen auf einer Leinwand zu haben. Dies führt dazu, dass der FPS deutlich sinkt, da die Anzahl der eingehenden Punkte auf Zehntausende steigt. Der direkte Umgang mit WebGL erhöht diese Leistung scheinbar um das 20- bis 50-fache. Ich versuche, die von dieser Person verwendete Rendermethode nachzuahmen: http://codeflow.org/entries/2013/feb/04/high-performance-js-heatmaps/ –

+0

Verfolgen Sie eine Helligkeitsvariable oder etwas Ähnliches. Es sollte bei 1,0 beginnen und dann im Laufe der Zeit auf 0 verringern. Senden Sie es an die GPU und multiplizieren Sie 'gl_FragColor' mit der Helligkeit. Es ist auch möglich, die Helligkeitsverringerung ganz auf GPU zu verschieben, aber ich bin mir nicht sicher, ob das das ist, wonach Sie suchen. – user80667

Antwort

0

Es ist schwer, eine Antwort zu geben, da gibt es an infinite number of ways to do it, aber im Grunde ist WebGL nur eine Raseration-API, also wenn Sie wollen, dass etwas Überstunden ausblendet, müssen Sie es jeden Rahmen rendern und im Laufe der Zeit Dinge zeichnen, die Sie mit einem ausblenden möchten mehr Transparenz.

In Pseudo-Code

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

Hier ist eine Leinwand 2D-Version zu halten es einfach

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 ist nicht anders, ersetzen Sie einfach ctx.clearRect mit gl.clear und drawCircle mit einigen Funktion, die einen Kreis zeichnet.

Hier ist die WebGL-Version des gleichen Programms

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>

Ich möchte nicht eine Matrix Bibliothek aber you can read about matrices here und weil fast jeder in Themen sind, wenn sie von einer Form/Shader-Abschluss zu 2 möchten Sie wahrscheinlich read this about drawing multiple things

+0

danke für so eine ausführliche Antwort. Ich werde versuchen, dies in meinem Projekt zu implementieren. Der erste Link, den Sie angegeben haben, ist übrigens genau der Effekt, nach dem ich suche. Wird in Ihrer WebGL-'process'-Funktion auch die' time' automatisch übergeben? Es scheint nicht für mich zu funktionieren. –

+0

'process' wird von' requestAnimationFrame' aufgerufen. 'reqeustAnimationFrame' übergibt die seit dem Laden der Seite in Millisekunden an die Funktion – gman

+0

perfekte Zeit. Vielen Dank! –