如何为WebGL着色器绘制的圆形/椭圆形(使用边缘/距离抗锯齿绘制)添加统一宽度的轮廓?[英] How can I add a uniform width outline to WebGL shader drawn circles/ellipses (drawn using edge/distance antialiasing)

本文是小编为大家收集整理的关于如何为WebGL着色器绘制的圆形/椭圆形(使用边缘/距离抗锯齿绘制)添加统一宽度的轮廓?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我是在WebGL中使用单四个和片段着色器在WebGL中绘制圆圈/椭圆形,以便以独立的方式绘制它们

这是我目前的片段着色器:

'#extension GL_OES_standard_derivatives : enable',
'precision mediump float;',
'varying vec2 coord;',
'vec4 circleColor = vec4(1.0, 0.5, 0.0, 1.0);',
'vec4 outlineColor = vec4(0.0, 0.0, 0.0, 1.0);',

'uniform float strokeWidth;',          
'float outerEdgeCenter = 0.5 - strokeWidth;',

'void main(void){',

      'float dx = 0.5 - coord.x;',
      'float dy = 0.5 - coord.y;',
      'float distance = sqrt(dx*dx + dy*dy);',

      'float delta = fwidth(distance);',
      'float alpha = 1.0 - smoothstep(0.45 - delta, 0.45, distance);',
      'float stroke = 1.0 - smoothstep(outerEdgeCenter - delta, outerEdgeCenter + delta, distance);',

      'gl_FragColor = vec4( mix(outlineColor.rgb, circleColor.rgb, stroke), alpha );',                  

'}'

这会创建一个带有黑色轮廓的橙色圆圈,无论大小如何,都完全抗衡.

在此处输入图像说明

然而,一旦我将四轮(比例比例)转换为将圆变成椭圆形,distance计算随之转换,从而导致轮廓也缩放.我的理解是,我需要以某种方式通过颠倒来解释四边形的变换.

在此处输入图像说明

我想要的是distance即使在四边形转换时保持均匀,实际上在整个圆圈/椭圆周围产生恒定的宽度轮廓.

任何帮助将不胜感激.

推荐答案

问题是,您的片段着色器(FS)现在是黑框(因为代码缺乏信息).

您的FS写入正方形空间内.因此,它总是在空间中渲染圆,其中x和y大小相同(-1.0; 1.0间隔).

虽然四方在外部(在VS或其他任何地方)进行了转换,并且无法反映FS中的转换.

为了解决问题,我建议将其他信息推入有关缩放的FS. Shadertoy 在着色器输入中提供:

uniform vec3 iResolution; // viewport resolution (in pixels)

除此之外,除了屏幕尺寸的分辨率,但有关四边形转换的信息,因此类似:

varying vec2 trans;
// where value (1.0, 1.0) mean no transformation

然后,您可以使用此值来计算不同的中风.我宁愿计算出唯一的dx和dy值.

还有更多方法可以实现工作解决方案,这取决于您以后要如何使用它(应该进行哪种转换等等).因此,我只提出了基本但最简单的解决方案.

本文地址:https://www.itbaoku.cn/post/2091114.html

问题描述

I am drawing circles/ellipses in WebGL using a single Quad and a fragment shader, in order to draw them in a resolution independent manner (Edge distance anti-aliasing)

Here is my fragment shader currently:

'#extension GL_OES_standard_derivatives : enable',
'precision mediump float;',
'varying vec2 coord;',
'vec4 circleColor = vec4(1.0, 0.5, 0.0, 1.0);',
'vec4 outlineColor = vec4(0.0, 0.0, 0.0, 1.0);',

'uniform float strokeWidth;',          
'float outerEdgeCenter = 0.5 - strokeWidth;',

'void main(void){',

      'float dx = 0.5 - coord.x;',
      'float dy = 0.5 - coord.y;',
      'float distance = sqrt(dx*dx + dy*dy);',

      'float delta = fwidth(distance);',
      'float alpha = 1.0 - smoothstep(0.45 - delta, 0.45, distance);',
      'float stroke = 1.0 - smoothstep(outerEdgeCenter - delta, outerEdgeCenter + delta, distance);',

      'gl_FragColor = vec4( mix(outlineColor.rgb, circleColor.rgb, stroke), alpha );',                  

'}'

This creates an orange circle with a black outline that is perfectly antialiased whatever the size.

enter image description here

However, as soon as I transform the quad (scale it) in order to turn the circle into an ellipse, the distance calculation transforms along with it, causing the outline to also scale. My understanding is that I would somehow need to account for the quad's transform by inverting it.

enter image description here

What I would like is for the distance to remain uniform even when the quad is transformed, in effect producing a constant width outline around the whole circle/ellipse.

Any Help would be greatly appreciated.

推荐答案

The problem is, your fragment shader (FS) is kind of blackbox now (becausethe the code is lack of information).

Your FS is written to work within square space. So it always render circle in space where x and y are same size ( -1.0; 1.0 interval).

While quad is transformed outside (in VS or anywhere else) and there is no way how to reflect that transformation in FS yet.

To solve the problem, I suggest to push an additional information into FS about the scaling. Something like Shadertoy provides in shader inputs:

uniform vec3 iResolution; // viewport resolution (in pixels)

except this wont be the resolution of the screen size, but the information about quad transformation, so something like:

varying vec2 trans;
// where value (1.0, 1.0) mean no transformation

Then you can use this value to calculate different stroke. Instead of inverting the transformation for the current stroke, I would rather calculate unique dx and dy values for it.

There are more ways to achieve the working solution and it depends on how do you want to use it later (what kinds of transformations should be possible etc.). So I present only the basic but the easiest solution.