在GLSL着色器中为线段和三角形实现抗锯齿逻辑

时间:2019-07-14 20:34:02

标签: three.js glsl webgl shader antialiasing

我正在基于Three.js构建2D Graph结构,该图的所有元素(节点,边,箭头的三角形)都是在着色器中计算的。我能够对节点(圆形)达到很好的抗锯齿水平,但是对于线和三角形却坚持了相同的任务。

遵循以下问题,我对包含和不包含笔触的节点(圆形)都能获得良好的抗锯齿效果:How can I add a uniform width outline to WebGL shader drawn circles/ellipses (drawn using edge/distance antialiasing),我的代码,负责抗锯齿alpha:

`float strokeWidth = 0.09;
 float outerEdgeCenter = 0.5 - strokeWidth;
 float d = distance(vUV, vec2(.5, .5));
 float delta = fwidth(d);
 float alpha = 1.0 - smoothstep(0.45 - delta, 0.45, d);
 float stroke = 1.0 - smoothstep(outerEdgeCenter - delta, 
 outerEdgeCenter + delta, d);` 

但是现在我完全堆叠了边缘和三角形来做同样的事情。

这是我现在拥有的形状图像的一个示例(在非视网膜显示器上):

arrow

line

要减少欠采样伪像,我想通过操纵alpha直接在着色器中做类似的算法(对于圆形),并且已经找到了与此主题相关的一些材料:

  1. https://thebookofshaders.com/glossary/?search=smoothstep-似乎是最接近的解决方案,但不幸的是我无法正确实现它,无法弄清楚如何为分段线建立y方程。

  2. https://discourse.threejs.org/t/shader-to-create-an-offset-inward-growing-stroke/6060/12-最后一个答案,看起来很有希望,但没有给我适当的结果。

  3. https://www.shadertoy.com/view/4dcfW8-也没有给出正确的结果。

以下是我的直线和三角形着色器的示例:

Line VertexShader(是WestLangley的LineMaterial着色器的稍微改编的版本):

`precision highp float;

#include <common>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>

uniform float linewidth;
uniform vec2 resolution;
attribute vec3 instanceStart;
attribute vec3 instanceEnd;
attribute vec3 instanceColorStart;
attribute vec3 instanceColorEnd;
attribute float alphaStart;
attribute float alphaEnd;
attribute float widthStart;
attribute float widthEnd;
varying vec2 vUv;
varying float alphaTest;

void trimSegment( const in vec4 start, inout vec4 end ) {

    // trim end segment so it terminates between the camera plane and the near plane
    // conservative estimate of the near plane
    float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column
    float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column
    float nearEstimate = - 0.5 * b / a;
    float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );

    end.xyz = mix( start.xyz, end.xyz, alpha );
}

void main() {                
    #ifdef USE_COLOR
        vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;
        alphaTest = ( position.y < 0.5 ) ? alphaStart : alphaEnd;
    #endif

        float aspect = resolution.x / resolution.y;
        vUv = uv;

        // camera space
        vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
        vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );

        // special case for perspective projection, and segments that terminate either in, or behind, the camera plane
        // clearly the gpu firmware has a way of addressing this issue when projecting into ndc space
        // but we need to perform ndc-space calculations in the shader, so we must address this issue directly
        // perhaps there is a more elegant solution -- WestLangley

        bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column

        if (perspective) {
            if (start.z < 0.0 && end.z >= 0.0) {
                trimSegment( start, end );
            } else if (end.z < 0.0 && start.z >= 0.0) {
                trimSegment( end, start );
            }
        }

        // clip space
        vec4 clipStart = projectionMatrix * start;
        vec4 clipEnd = projectionMatrix * end;

        // ndc space
        vec2 ndcStart = clipStart.xy / clipStart.w;
        vec2 ndcEnd = clipEnd.xy / clipEnd.w;

        // direction
        vec2 dir = ndcEnd - ndcStart;

        // account for clip-space aspect ratio
        dir.x *= aspect;
        dir = normalize( dir );

        // perpendicular to dir
        vec2 offset = vec2( dir.y, - dir.x );

        // undo aspect ratio adjustment
        dir.x /= aspect;
        offset.x /= aspect;

        // sign flip
        if ( position.x < 0.0 ) offset *= - 1.0;

        // endcaps, to round line corners
        if ( position.y < 0.0 ) {
           // offset += - dir;
        } else if ( position.y > 1.0 ) {
           // offset += dir;
        }

        // adjust for linewidth
        offset *= (linewidth * widthStart);

        // adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...
        offset /= resolution.y;

        // select end
        vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;

        // back to clip space
        offset *= clip.w;

        clip.xy += offset;

        gl_Position = clip;

        vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation

        #include <logdepthbuf_vertex>
        #include <clipping_planes_vertex>
        #include <fog_vertex>
   }`

Line FragmentShader:

`precision highp float;

   #include <common>
       #include <color_pars_fragment>
       #include <fog_pars_fragment>
       #include <logdepthbuf_pars_fragment>
       #include <clipping_planes_pars_fragment>

   uniform vec3 diffuse;
       uniform float opacity;
       varying vec2 vUv;
       varying float alphaTest;

   void main() {                   
     if ( abs( vUv.y ) > 1.0 ) {
                 float a = vUv.x;
                 float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
                 float len2 = a * a + b * b;

                 if ( len2 > 1.0 ) discard;
           }

           vec4 diffuseColor = vec4( diffuse, alphaTest );

           #include <logdepthbuf_fragment>
           #include <color_fragment>

           gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a );

           #include <premultiplied_alpha_fragment>
           #include <tonemapping_fragment>
           #include <encodings_fragment>
           #include <fog_fragment>

   }`

三角形顶点着色器:

`precision highp float;

       uniform mat4 modelViewMatrix;
       uniform mat4 projectionMatrix;
       uniform float zoomLevel;
       attribute vec3 position;
       attribute vec3 vertexPos;
       attribute vec3 color;
       attribute float alpha;
       attribute float xAngle;
       attribute float yAngle;
       attribute float xScale;
       attribute float yScale;
       varying vec4 vColor;

       // transforms the 'positions' geometry with instance attributes
       vec3 transform( inout vec3 position, vec3 T) {

            position.x *= xScale;
            position.y *= yScale;

            // Rotate the position
            vec3 rotatedPosition = vec3(
               position.x * yAngle + position.y * xAngle,
               position.y * yAngle - position.x * xAngle, 0);

            position = rotatedPosition + T;

            // return the transformed position
            return position;
       }

       void main() {                
            vec3 pos = position;
            vColor = vec4(color, alpha);

            // transform it
            transform(pos, vertexPos);

            gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 );
       }`

三角形FragmentShader:

`precision highp float;
       varying vec4 vColor;

       void main() {                    
         gl_FragColor = vColor;
       }`

我们将非常感谢您提供有关此操作的任何帮助或对进一步研究提出正确建议的建议。谢谢!

0 个答案:

没有答案