【问题标题】:Change behavior of WebGl Screen-Space Projected Lines shader during zoom在缩放期间更改 WebGl 屏幕空间投影线着色器的行为
【发布时间】:2019-11-01 01:15:57
【问题描述】:

我正在构建基于 Three.js 的 2D 图形结构,并在相机缩放期间遇到与屏幕空间投影线行为相关的问题。问题是当我放大时线条变得明显更小,而在缩小时线条变得更大。

例子:

具有预定义粗细的法线:

缩小后的线条:

放大后的线条:

我也在着色器中构建的所有其他元素(圆形、箭头矩形)具有“正常”行为,并根据相机位置线性和相反方向改变它们的大小(放大时变大,缩小时变小)。我需要在着色器中使用线条达到完全相同的行为,但不知道该怎么做,因为在这个领域很新。

我的线条顶点着色器是 WestLangley 的 LineMaterial 着色器的略微改编版本,您可以查看下面的代码。我注意到的一个观察结果:

如果我删除 dir = normalize (dir) 线,线的缩放行为会变得正常,但它们的粗细开始取决于节点之间的距离,这也是不合适的。

这是顶点着色器:

      `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>
       }`

片段着色器:

`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>

       }`

非常感谢有关如何做到这一点的任何帮助。谢谢!

【问题讨论】:

    标签: three.js glsl shader vertex-shader


    【解决方案1】:

    未经测试的想法,基于您的 cmets 关于normalize() 调用的影响。交换这些行的顺序:

    dir = normalize( dir );
    vec2 offset = vec2( dir.y, - dir.x );
    

    变成

    vec2 offset = vec2( dir.y, - dir.x );
    dir = normalize( dir );
    

    所以offset 仍然取决于dir 的原始长度(我希望这应该使可见线宽正常运行),并且您仍然具有规范化(我希望这应该使可见线长度正常运行)。

    【讨论】:

    • 没关系,我误解了你的 cmets 关于厚度取决于节点分离 - 这个答案可能是错误的。也许最简单的解决方案是通过缩放因子统一缩放线宽。
    • 完美,感谢克劳德,这完全符合我的预期。对于所有对解决方案感兴趣的人:line offset *= (linewidth * widthStart) 应通过 scaleFactor 扩展:offset *= (linewidth * widthStart * scaleFactor); 其中 scaleFactor 是缩放系数(在我的情况下为 translate.k,因为我使用 D3.js 进行 2D 画布缩放)。
    猜你喜欢
    • 2016-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-30
    • 2013-05-02
    • 2012-04-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多