本文介绍了将抗锯齿的圆与regl混合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用regl渲染圈子,并且有三个目标:

I'm rendering circles using regl, and have three goals:

  1. 画布应该是透明的,并在其后面显示HTML内容.
  2. 圆应平滑地抗锯齿.
  3. 重叠的圆圈应看起来合理(混合颜色,无角)

到目前为止,我有:Glitch 代码演示.

So far, I have this: Glitch code and demo.

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

但是,结果看起来不太好.角是可见的,圆不能正确地融合.

However, the result looks not-so-great. Corners are visible, and circles aren't blending properly.

我还尝试添加以下混合术语:

I've also tried adding the following blend terms:

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

这看起来好一些,但是角落仍然存在,并且背景为白色时出了点问题.

This looks a bit better, but the corners are still there and something is wrong when the background is white.

您能对此提出建议吗? (如果可能的话,也许我会向我提供关于混合的更好信息),谢谢!

Could you suggest improvements to this? (And maybe point me to better information about blending, if that's what I'm missing here) Thanks!

推荐答案

您应该像这样设置混合参数:

You should set up your blending parameters like this:

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

这意味着您的目标颜色和源颜色将像这样混合:

This means that your destination and source color become blended like this:

红色,绿色和蓝色(srcRGB: 'src alpha'dstRGB: 'one minus src alpha'):

Red, green and blue (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

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

The alpha channel (srcAlpha: 'src alpha', dstAlpha: 'one minus src alpha'):

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

seel还 glBlendFunc glBlendFuncSeparate

此外,您还必须确保深度测试已禁用

请参阅上面的WebGL示例(已在Firefox,Chrome,Edge,Opera上测试):

See the WebGL example above (tested with Firefox, Chrome, Edge, 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>

这篇关于将抗锯齿的圆与regl混合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-21 12:56