【问题标题】:Efficiently iterate trough neighbouring pixels in fragment shader有效地遍历片段着色器中的相邻像素
【发布时间】:2019-08-24 19:33:09
【问题描述】:

对于上下文:

我正在使用 OpenFrameworks 制作生成式 2D 动画。 我正在尝试实现一个着色器,根据形状边缘的方向,用颜色填充一些形状。

基本上它需要一张像这样的图像:

然后吐出这样的东西:

请注意,它故意只采用形状左侧的颜色。

现在我的片段着色器看起来像这样:

#version 150

out vec4 outputColor;

uniform sampler2DRect fbo;
uniform sampler2DRect mask;

vec2 point;
vec4 col;
float x,i;
float delta = 200;

vec4 extrudeColor()
{
        x = gl_FragCoord.x > delta ? gl_FragCoord.x - delta : 0;
        for(i = gl_FragCoord.x; i > x; i--)
        {
            point = vec2(i, gl_FragCoord.y);
            if(texture(fbo, point) != vec4(0,0,0,0)){
                col = texture(fbo, point);
                return vec4(col.r, col.g, col.b, (i-x)/delta);
            }
        }
        return vec4(0,0,0,1);
}

void main()
{
    outputColor = texture(mask, gl_FragCoord.xy) == vec4(1,1,1,1) && texture(fbo, gl_FragCoord.xy) == vec4(0,0,0,0) ? extrudeColor() : vec4(0,0,0,0);
}

mask 采样器只是第二张图像的黑白版本,我用它来避免计算形状之外的像素。

我的着色器可以工作,但速度很慢,而且我觉得我没有使用正确的 GPU 思维和编码。

实际的、更一般的问题:

我对 glsl 和 opengl 完全陌生。有没有一种方法可以更有效地通过相邻像素进行这种迭代,而无需读取这么多的texture()

也许使用矩阵? IDK!

【问题讨论】:

    标签: c++ opengl glsl opengl-3


    【解决方案1】:

    这是解决此问题的一种非常低效的方法。尽量避免在着色器中使用条件(if)和循环(for)。我建议加载或生成单个纹理,然后使用 alpha 蒙版创建您需要的形状。纹理可以保持不变,而每帧可以生成 2 位或 8 位掩码。

    另一种方法是使用一些制服并在数组中上传“每行”数据:

    #version 440 core
    
    uniform sampler2D _Texture ; // The texture to draw
    uniform vec2 _Position ; // The 'object' position (screen coordinate)
    uniform int _RowOffset ; // The offset in the 'object' to start drawing
    uniform int _RowLength ; // The length of the 'row' to draw
    uniform int _Height ; // The height of the 'object' to draw
    
    in vec2 _TexCoord ; // The texture coordinate passed from Vertex shader
    
    out vec4 _FragColor ; // The output color (gl_FragColor deprecated)
    
    void main () {
      if (gl_FragCoord.x < (_Position.x + _RowOffset)) discard ;
      if (gl_FragCoord.x > (_Position.x + _RowOffset + _RowLength)) discard ;
      _FragColor = texture2D (_Texture, _TexCoord.st) ;
    }
    

    或者,完全不采样纹理,您可以生成线性渐变函数并使用 Y 坐标从中采样颜色:

    const vec4 _Red = vec4 (1, 0, 0, 1) ;
    const vec4 _Green vec4 (0, 1, 0, 0) ;
    
    vec4 _GetGradientColor (float _P /* Percentage */) {
      float _R = (_Red.r * _P + _Green.r * (1 - _P) / 2 ;
      float _G = (_Red.g * _P + _Green.g * (1 - _P) / 2 ;
      float _B = (_Red.b * _P + _Green.b * (1 - _P) / 2 ;
      float _A = (_Red.a * _P + _Green.a * (1 - _P) / 2 ;
      return (vec4 (_R, _G, _B, _A)) ;
    }
    

    然后在你的 Frag Shader 中,

    float _P = gl_FragCoord.y - _Position.y / _Height ;
    _FragColor = _GetGradientColor (_P) ;
    

    Shader Output

    当然,这一切都可以稍微优化一下,这只会生成 2 色渐变,而看起来您需要几种颜色。在 Google 上快速搜索“线性梯度生成器”可以为您找到一些更好的选择。我还应该注意这个简单的示例不适用于其中带有“孔”的形状,但可以对其进行修改。如果着色器数学过于繁重,请选择带有 alpha 蒙版的纹理选项。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-30
      • 2014-05-30
      • 1970-01-01
      • 2011-02-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多