2017-07-12 303 views
1

我使用REGL渲染圈,並有三個目標:混合抗鋸齒圈與REGL

  1. 畫布應該是透明的,可見其背後的HTML內容。
  2. 圓圈應平滑抗鋸齒。
  3. 重疊的圓看起來應該是合理的(混合色,無邊角顯示)

到目前爲止,我有這樣的:毛刺codedemo

更新:演示鏈接現在反映工作,接受的答案。下面的代碼不變。

index.js

const regl = require('regl'); 
const glsl = require('glslify'); 
const vertexShader = glsl.file('../shaders/vertex.glsl'); 
const fragmentShader = glsl.file('../shaders/fragment.glsl'); 

// Create webgl context and clear. 
const canvasEl = document.querySelector('canvas'); 
const app = regl({ 
    canvas: canvasEl, 
    extensions: ['OES_standard_derivatives'] 
}); 
app.clear({color: [0, 0, 0, 0], depth: 1}); 

// Generate random points and colors. 
const attributes = {position: [], color: []}; 
for (let i = 0; i < 100; i++) { 
    attributes.position.push(Math.random() * 2 - 1, Math.random() * 2 - 1); 
    attributes.color.push(Math.random(), Math.random(), Math.random()); 
} 

// Define draw instructions. 
const draw = app({ 
    vert: vertexShader, 
    frag: fragmentShader, 
    attributes: attributes, 
    count: 100, 
    primitive: 'points', 
    depth: {enable: true}, 
    blend: { 
    enable: true 
    } 
}); 

// Draw the points. 
draw(); 

vertex.glsl

// vertex.glsl 
precision mediump float; 

attribute vec2 position; 
attribute vec3 color; 

varying vec3 vColor; 

void main() { 
    vColor = color; 
    gl_Position = vec4(position, 0, 1); 
    gl_PointSize = 40.; 
} 

fragment.glsl

// fragment.glsl 
#ifdef GL_OES_standard_derivatives 
#extension GL_OES_standard_derivatives : enable 
#endif 

precision mediump float; 

varying vec3 vColor; 

void main() { 

    float r = 0.0, delta = 0.0, alpha = 1.0; 
    vec2 cxy = 2.0 * gl_PointCoord - 1.0; 
    r = dot(cxy, cxy); 

#ifdef GL_OES_standard_derivatives 
    delta = fwidth(r); 
    alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, r); 
#endif 

    gl_FragColor = vec4(vColor, alpha); 
} 

但是,結果看起來並不太好。角落是可見的,並且圈子沒有正確混合。

circles with default blending

我也試着加入以下條款交融:

func: { 
    srcRGB: 'src alpha', 
    srcAlpha: 'one minus src alpha', 
    dstRGB: 'one minus src alpha', 
    dstAlpha: 'src alpha' 
} 

circles with custom blending

這看起來好一點,但邊角依然存在,什麼是錯的當背景是白色的。

你能否提出改進建議? (也許指向我更好的信息混合,如果這就是我在這裏失蹤)謝謝!

回答

1

你必須讓你的混淆參數是這樣的:

func: { 
    srcRGB: 'src alpha', 
    srcAlpha: 'src alpha', 
    dstRGB: 'one minus src alpha', 
    dstAlpha: 'one minus src alpha' 
} 

這意味着你的目標和源顏色變混合是這樣的:

紅,綠,藍(srcRGB: 'src alpha'dstRGB: 'one minus src alpha'):

R_dest = R_dest * (1 - Alpha_src) + R_src * Alpha_src 
G_dest = G_dest * (1 - Alpha_src) + G_src * Alpha_src 
B_dest = R_dest * (1 - Alpha_src) + R_src * Alpha_src 

阿爾法通道(srcAlpha: 'src alpha'dstAlpha: 'one minus src alpha'):

Alpha_dest = Alpha_dest * (1 - Alpha_src) + Alpha_src * Alpha_src 

SEEL也glBlendFuncglBlendFuncSeparate

而且你必須確保深度測試被禁用

見上面的WebGL的例子(與火狐,Chrome,邊緣,Opera上進行測試) :

<script type="text/javascript"> 
 

 
back_vert = 
 
"precision mediump float; \n" + 
 
"attribute vec2 inPos; \n" + 
 
"varying vec2 pos; \n" + 
 
"uniform mat4 u_projectionMat44;" + 
 
"uniform mat4 u_modelViewMat44;" + 
 
"void main()" + 
 
"{" + 
 
" pos   = inPos.xy;" + 
 
" vec4 viewPos = u_modelViewMat44 * vec4(inPos.xy, 0.0, 1.0);" + 
 
" gl_Position = u_projectionMat44 * viewPos;" + 
 
"}"; 
 

 
back_frag = 
 
"precision mediump float; \n" + 
 
"varying vec2 pos; \n" + 
 
"void main() \n" + 
 
"{ \n" + 
 
" vec2 coord = pos * 0.5 + 0.5; \n" + 
 
" float gray = smoothstep(0.3, 0.7, (coord.x + coord.y) * 0.5); \n" + 
 
" gl_FragColor = vec4(vec3(gray), 1.0); \n" + 
 
"}"; 
 

 
draw_vert = 
 
"precision mediump float; \n" + 
 
"attribute vec2 inPos; \n" + 
 
"varying vec2 pos; \n" + 
 
"uniform mat4 u_projectionMat44;" + 
 
"uniform mat4 u_modelViewMat44;" + 
 
"void main()" + 
 
"{" + 
 
" pos   = inPos.xy;" + 
 
" vec4 viewPos = u_modelViewMat44 * vec4(inPos.xy, 0.0, 1.0);" + 
 
" gl_Position = u_projectionMat44 * viewPos;" + 
 
"}"; 
 

 
draw_frag = 
 
"precision mediump float; \n" + 
 
"varying vec2 pos; \n" + 
 
"uniform vec4 u_color;" + 
 
"uniform vec2 u_vp;" + 
 
"void main()" + 
 
"{" + 
 
" float r = length(pos);" + 
 
" float d = 4.0 * length(1.0/u_vp); \n" + 
 
" float a = 1.0 - smoothstep(1.0 - d, 1.0 + d, r); \n" + 
 
" gl_FragColor = vec4(u_color.rgb, u_color.a * a);" + 
 
"}"; 
 

 
glArrayType = typeof Float32Array !="undefined" ? Float32Array : (typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array); 
 

 
function IdentityMat44() { 
 
    var m = new glArrayType(16); 
 
    m[0] = 1; m[1] = 0; m[2] = 0; m[3] = 0; 
 
    m[4] = 0; m[5] = 1; m[6] = 0; m[7] = 0; 
 
    m[8] = 0; m[9] = 0; m[10] = 1; m[11] = 0; 
 
    m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1; 
 
    return m; 
 
}; 
 

 
function RotateAxis(matA, angRad, axis) { 
 
    var aMap = [ [1, 2], [2, 0], [0, 1] ]; 
 
    var a0 = aMap[axis][0], a1 = aMap[axis][1]; 
 
    var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad); 
 
    var matB = new glArrayType(16); 
 
    for (var i = 0; i < 16; ++ i) matB[i] = matA[i]; 
 
    for (var i = 0; i < 3; ++ i) { 
 
     matB[a0*4+i] = matA[a0*4+i] * cosAng + matA[a1*4+i] * sinAng; 
 
     matB[a1*4+i] = matA[a0*4+i] * -sinAng + matA[a1*4+i] * cosAng; 
 
    } 
 
    return matB; 
 
} 
 

 
function Translate(matA, trans) { 
 
    var matB = new glArrayType(16); 
 
    for (var i = 0; i < 16; ++ i) matB[i] = matA[i]; 
 
    for (var i = 0; i < 3; ++ i) 
 
     matB[12+i] = matA[i] * trans[0] + matA[4+i] * trans[1] + matA[8+i] * trans[2] + matA[12+i]; 
 
    return matB; 
 
} 
 

 
function Scale(matA, scale) { 
 
    var matB = new glArrayType(16); 
 
    for (var i = 0; i < 16; ++ i) matB[i] = matA[i]; 
 
    for (var a = 0; a < 4; ++ a) 
 
     for (var i = 0; i < 3; ++ i) 
 
      matB[a*4+i] = matA[a*4+i] * scale[0]; 
 
    return matB; 
 
} 
 

 
Ortho = function(l, r, t, b, n, f) { 
 
    var fn = f + n; 
 
    var f_n = f - n; 
 
    var m = IdentityMat44(); 
 
    m[0] = 2/(r-l); m[1] = 0;  m[2] = 0;  m[3] = 0; 
 
    m[4] = 0;  m[5] = 2/(t-b); m[6] = 0;  m[7] = 0; 
 
    m[8] = 0;  m[9] = 0;  m[10] = -2/f_n; m[11] = -fn/f_n; 
 
    m[12] = 0;  m[13] = 0;  m[14] = 0;  m[15] = 1; 
 
    return m; 
 
} 
 

 
vec4_add = function(a, b) { return [ a[0]+b[0], a[1]+b[1], a[2]+b[2], a[3]+b[3] ]; } 
 
vec4_sub = function(a, b) { return [ a[0]-b[0], a[1]-b[1], a[2]-b[2], a[3]-b[3] ]; } 
 
vec4_mul = function(a, b) { return [ a[0]*b[0], a[1]*b[1], a[2]*b[2], a[3]*b[3] ]; } 
 
vec4_scale = function(a, s) { return [ a[0]*s, a[1]*s, a[2]*s, a[3]*s ]; } 
 

 
// shader program object 
 
var ShaderProgram = {}; 
 
ShaderProgram.Create = function(shaderList, uniformNames) { 
 
    var shaderObjs = []; 
 
    for (var i_sh = 0; i_sh < shaderList.length; ++ i_sh) { 
 
     var shderObj = this.CompileShader(shaderList[i_sh].source, shaderList[i_sh].stage); 
 
     if (shderObj == 0) 
 
      return 0; 
 
     shaderObjs.push(shderObj); 
 
    } 
 
    var progObj = this.LinkProgram(shaderObjs) 
 
    if (progObj != 0) { 
 
     progObj.unifomLocation = {}; 
 
     for (var i_n = 0; i_n < uniformNames.length; ++ i_n) { 
 
      var name = uniformNames[i_n]; 
 
      progObj.unifomLocation[name] = gl.getUniformLocation(progObj, name); 
 
     } 
 
    } 
 
    return progObj; 
 
} 
 
ShaderProgram.Use = function(progObj) { gl.useProgram(progObj); } 
 
ShaderProgram.SetUniformInt = function(progObj, name, val) { gl.uniform1i(progObj.unifomLocation[name], val); } 
 
ShaderProgram.SetUniform2f = function(progObj, name, arr) { gl.uniform2fv(progObj.unifomLocation[name], arr); } 
 
ShaderProgram.SetUniform3f = function(progObj, name, arr) { gl.uniform3fv(progObj.unifomLocation[name], arr); } 
 
ShaderProgram.SetUniform4f = function(progObj, name, arr) { gl.uniform4fv(progObj.unifomLocation[name], arr); } 
 
ShaderProgram.SetUniformMat44 = function(progObj, name, mat) { gl.uniformMatrix4fv(progObj.unifomLocation[name], false, mat); } 
 
ShaderProgram.CompileShader = function(source, shaderStage) { 
 
    var shaderObj = gl.createShader(shaderStage); 
 
    gl.shaderSource(shaderObj, source); 
 
    gl.compileShader(shaderObj); 
 
    var status = gl.getShaderParameter(shaderObj, gl.COMPILE_STATUS); 
 
    if (!status) alert(gl.getShaderInfoLog(shaderObj)); 
 
    return status ? shaderObj : 0; 
 
} 
 
ShaderProgram.LinkProgram = function(shaderObjs) { 
 
    var prog = gl.createProgram(); 
 
    for (var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh) 
 
     gl.attachShader(prog, shaderObjs[i_sh]); 
 
    gl.linkProgram(prog); 
 
    status = gl.getProgramParameter(prog, gl.LINK_STATUS); 
 
    if (!status) alert("Could not initialise shaders"); 
 
    gl.useProgram(null); 
 
    return status ? prog : 0; 
 
} 
 
     
 

 
function drawScene(){ 
 

 
    var canvas = document.getElementById("camera-canvas"); 
 
    var vp = [canvas.width, canvas.height]; 
 
    var currentTime = Date.now(); 
 
    var deltaMS = currentTime - startTime; 
 
    var aspect = canvas.width/canvas.height; 
 
    var matOrtho = Ortho(-aspect, aspect, 1, -1, -1, 1); 
 
    var alpha = document.getElementById("alpha").value/100; 
 
     
 
    gl.viewport(0, 0, canvas.width, canvas.height); 
 
    gl.disable(gl.DEPTH_TEST); 
 
    gl.clearColor(0.0, 0.0, 0.0, 1.0); 
 
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 
 
    gl.disable(gl.BLEND); 
 
    ShaderProgram.Use(progBack); 
 
    ShaderProgram.SetUniformMat44(progBack, "u_projectionMat44", matOrtho); 
 
    ShaderProgram.SetUniformMat44(progBack, "u_modelViewMat44", IdentityMat44()); 
 
    gl.enableVertexAttribArray(progBack.inPos); 
 
    gl.bindBuffer(gl.ARRAY_BUFFER, bufObj.pos); 
 
    gl.vertexAttribPointer(progBack.inPos, 2, gl.FLOAT, false, 0, 0); 
 
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx); 
 
    gl.drawElements(gl.TRIANGLES, bufObj.inx.len, gl.UNSIGNED_SHORT, 0); 
 
    gl.disableVertexAttribArray(progBack.pos); 
 

 
    gl.enable(gl.BLEND); 
 
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); 
 
    ShaderProgram.Use(progDraw); 
 
    gl.enableVertexAttribArray(progDraw.inPos); 
 
    gl.bindBuffer(gl.ARRAY_BUFFER, bufObj.pos); 
 
    gl.vertexAttribPointer(progDraw.inPos, 2, gl.FLOAT, false, 0, 0); 
 
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx); 
 
    ShaderProgram.SetUniformMat44(progDraw, "u_projectionMat44", matOrtho); 
 
    ShaderProgram.SetUniform2f(progDraw, "u_vp", vp); 
 
     
 
    var col = [ [1.0,0.0,0.0], [1.0,1.0,0.0], [0.0,0.0,1.0] ]; 
 
    var time = [ 7.0, 11.0, 13.0 ]; 
 
    for (var i = 0; i < 3; ++ i) {  
 
     var modelMat = Scale(IdentityMat44(), [ 0.3, 0.3, 0.3]); 
 
     var angRad = CalcAng(currentTime, time[i]) + i * Math.PI * 2/3; 
 
     var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad); 
 
     modelMat[12] = cosAng * 0.3 + i * 0.2; 
 
     modelMat[13] = sinAng * 0.3 + i * 0.2; 
 
     
 
     ShaderProgram.SetUniformMat44(progDraw, "u_modelViewMat44", modelMat); 
 
     var color = col[i]; 
 
     color.push(alpha); 
 
     ShaderProgram.SetUniform4f(progDraw, "u_color", color); 
 
     gl.drawElements(gl.TRIANGLES, bufObj.inx.len, gl.UNSIGNED_SHORT, 0); 
 
    } 
 
    gl.disableVertexAttribArray(progDraw.pos); 
 
} 
 

 
var startTime; 
 
function Fract(val) { 
 
    return val - Math.trunc(val); 
 
} 
 
function CalcAng(currentTime, intervall) { 
 
    return Fract((currentTime - startTime)/(1000*intervall)) * 2.0 * Math.PI; 
 
} 
 
function CalcMove(currentTime, intervall, range) { 
 
    var pos = self.Fract((currentTime - startTime)/(1000*intervall)) * 2.0 
 
    var pos = pos < 1.0 ? pos : (2.0-pos) 
 
    return range[0] + (range[1] - range[0]) * pos; 
 
}  
 

 
var mousePos = [-1, -1]; 
 
var gl; 
 
var prog; 
 
var bufObj = {}; 
 
function cameraStart() { 
 

 
    var canvas = document.getElementById("camera-canvas"); 
 
    gl = canvas.getContext("experimental-webgl"); 
 
    if (!gl) 
 
     return; 
 
    var vp = [canvas.width, canvas.height]; 
 

 
    progBack = ShaderProgram.Create( 
 
     [ { source : back_vert, stage : gl.VERTEX_SHADER }, 
 
     { source : back_frag, stage : gl.FRAGMENT_SHADER } 
 
     ], 
 
     [ "u_projectionMat44", "u_modelViewMat44"]); 
 
    progBack.inPos = gl.getAttribLocation(progBack, "inPos"); 
 
    if (progBack == 0) 
 
     return; 
 

 
    progDraw = ShaderProgram.Create( 
 
     [ { source : draw_vert, stage : gl.VERTEX_SHADER }, 
 
     { source : draw_frag, stage : gl.FRAGMENT_SHADER } 
 
     ], 
 
     [ "u_projectionMat44", "u_modelViewMat44", "u_color", "u_alpha", "u_vp"]); 
 
    progDraw.inPos = gl.getAttribLocation(progDraw, "inPos"); 
 
    if (progDraw == 0) 
 
     return; 
 

 
    var pos = [ -1, -1, 1, -1, 1, 1, -1, 1 ]; 
 
    var inx = [ 0, 1, 2, 0, 2, 3 ]; 
 
    bufObj.pos = gl.createBuffer(); 
 
    gl.bindBuffer(gl.ARRAY_BUFFER, bufObj.pos); 
 
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pos), gl.STATIC_DRAW); 
 
    bufObj.inx = gl.createBuffer(); 
 
    bufObj.inx.len = inx.length; 
 
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx); 
 
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(inx), gl.STATIC_DRAW); 
 

 
    startTime = Date.now(); 
 
    setInterval(drawScene, 50); 
 
} 
 

 
</script> 
 

 
<body onload="cameraStart();"> 
 
    <div style="margin-left: 260px;"> 
 
     <div style="float: right; width: 100%; background-color: #CCF;"> 
 
      <form name="inputs"> 
 
       <table> 
 
        <tr> <td> alpha </td> 
 
         <td> <input type="range" id="alpha" min="0" max="100" value="50"/></td> </tr> 
 
       </table> 
 
      </form> 
 
     </div> 
 
     <div style="float: right; width: 260px; margin-left: -260px;"> 
 
      <canvas id="camera-canvas" style="border: none;" width="256" height="256"></canvas> 
 
     </div> 
 
     <div style="clear: both;"></div> 
 
    </div> 
 
</body>

+1

謝謝你的詳細的答案!我接受了這個,因爲混合參數最適合我的情況。但對於未來的讀者,[gman的回答](https://stackoverflow.com/a/45074112/1314762)中提到的預乘alpha也是必要的。 –

1

帆布需要預乘alpha,除非您特別要求,否則這樣的問題#1是你的混合功能應該是

blend: { 
    enable: true, 
    func: { 
     srcRGB: 'one', 
     srcAlpha: 'one', 
     dstRGB: 'one minus src alpha', 
     dstAlpha: 'one minus src alpha', 
    }, 
    }, 

您還需要從您的着色器返回預乘值

gl_FragColor = vec4(vColor, alpha); 
    gl_FragColor.rgb *= gl_FragColor.a; // premultiply by alpha 
} 

的其他問題是你有深度測試。默認的深度函數是LESS。這意味着新的像素不會被繪製,除非它們的Z值比現有的Z值小。由於您的所有圓都以相同的深度繪製,因此在繪製前一個圓的任何位置都不會繪製新的圓。

最簡單的解決方法是關閉深度測試

depth: {enable: false}, 

結果:

enter image description here

至於爲什麼預乘alpha看到

https://developer.nvidia.com/content/alpha-blending-pre-or-not-pre

+0

Upvoted,但我已經接受了其他答案;兩者都提供了有用的解謝謝! –