gpt4 book ai didi

android - 如何在 OpenGL 中使用 fragment 着色器制作更平滑的边框?

转载 作者:行者123 更新时间:2023-12-04 23:49:48 26 4
gpt4 key购买 nike

我一直在尝试使用 Android 中的 OpenGL 绘制具有透明背景的图像的边框。我正在使用 fragment 着色器和顶点着色器。 (From the GPUImage Library)
下面我添加了图A和图B。
Fig A. with rough border
图 A。
Fig B. with smooth border
图 B。
我已经实现了图 A。使用自定义的 fragment 着色器。但是无法使边框更平滑,如图 B 所示。我附上了我使用过的着色器代码(以实现粗糙的边框)。这里有人可以帮助我如何使边界更平滑吗?
这是我的顶点着色器:

    attribute vec4 position;
attribute vec4 inputTextureCoordinate;
varying vec2 textureCoordinate;

void main()
{
gl_Position = position;
textureCoordinate = inputTextureCoordinate.xy;
}
这是我的 fragment 着色器:
我计算了当前像素周围的 8 个像素。如果这 8 个像素中的任何一个像素是不透明的(alpha 大于 0.4),它就会被绘制为边框颜色。
                precision mediump float;
uniform sampler2D inputImageTexture;
varying vec2 textureCoordinate;
uniform lowp float thickness;
uniform lowp vec4 color;

void main() {
float x = textureCoordinate.x;
float y = textureCoordinate.y;
vec4 current = texture2D(inputImageTexture, vec2(x,y));

if ( current.a != 1.0 ) {
float offset = thickness * 0.5;

vec4 top = texture2D(inputImageTexture, vec2(x, y - offset));
vec4 topRight = texture2D(inputImageTexture, vec2(x + offset,y - offset));
vec4 topLeft = texture2D(inputImageTexture, vec2(x - offset, y - offset));
vec4 right = texture2D(inputImageTexture, vec2(x + offset, y ));
vec4 bottom = texture2D(inputImageTexture, vec2(x , y + offset));
vec4 bottomLeft = texture2D(inputImageTexture, vec2(x - offset, y + offset));
vec4 bottomRight = texture2D(inputImageTexture, vec2(x + offset, y + offset));
vec4 left = texture2D(inputImageTexture, vec2(x - offset, y ));

if ( top.a > 0.4 || bottom.a > 0.4 || left.a > 0.4 || right.a > 0.4 || topLeft.a > 0.4 || topRight.a > 0.4 || bottomLeft.a > 0.4 || bottomRight.a > 0.4 ) {
if (current.a != 0.0) {
current = mix(color , current , current.a);
} else {
current = color;
}
}
}

gl_FragColor = current;
}

最佳答案

你几乎走在了正确的轨道上。
主要算法是:

  • 模糊图像。
  • 使用不透明度高于某个阈值的像素作为轮廓。

  • 主要问题是模糊步骤。它需要大而平滑的模糊才能获得您想要的平滑轮廓。对于模糊,我们可以使用卷积滤波器 Kernel .而要实现大模糊,我们应该使用大内核。我建议使用 Gaussian Blur分布,因为它是众所周知和使用的。

    他算法的概述是:
    对于每个 fragment ,我们对其周围的许多位置进行采样。样本是在 N×N 网格中制作的。我们使用 2D Gaussian Distribution 之后的权重对它们进行平均。 .
    这会导致图像模糊。
    对于模糊的图像,我们用轮廓颜色绘制 alpha 大于阈值的 fragment 。当然,原始图像中的任何不透明像素也应该出现在结果中。

    在旁注中,您的解决方案几乎是 3 x 3 内核的模糊(您在 3 x 3 网格中对 fragment 周围的位置进行采样)。但是,3 x 3 内核无法提供所需的模糊量。您需要更多样本(例如 11 x 11)。此外,靠近中心的权重应该对结果产生更大的影响。因此,统一的权重不会很好地工作。

    哦,还有一件更重要的事情:
    一个单一的着色器来完成这并不是实现它的最快方法。通常,这将通过 2 个单独的渲染来完成。第一个将照常渲染图像,第二个渲染将模糊并添加轮廓。我假设你想用 1 个单一的渲染来做到这一点。
    以下是完成此操作的顶点和 fragment 着色器:
    顶点着色器
    varying vec2 vecUV;
    varying vec3 vecPos;
    varying vec3 vecNormal;

    void main() {
    vecUV = uv * 3.0 - 1.0;
    vecPos = (modelViewMatrix * vec4(position, 1.0)).xyz;
    vecNormal = (modelViewMatrix * vec4(normal, 0.0)).xyz;

    gl_Position = projectionMatrix * vec4(vecPos, 1.0);
    }
    fragment 着色器

    precision highp float;

    varying vec2 vecUV;
    varying vec3 vecPos;
    varying vec3 vecNormal;

    uniform sampler2D inputImageTexture;


    float normalProbabilityDensityFunction(in float x, in float sigma)
    {
    return 0.39894*exp(-0.5*x*x/(sigma*sigma))/sigma;
    }

    vec4 gaussianBlur()
    {
    // The gaussian operator size
    // The higher this number, the better quality the outline will be
    // But this number is expensive! O(n2)
    const int matrixSize = 11;

    // How far apart (in UV coordinates) are each cell in the Gaussian Blur
    // Increase this for larger outlines!
    vec2 offset = vec2(0.005, 0.005);

    const int kernelSize = (matrixSize-1)/2;
    float kernel[matrixSize];

    // Create the 1-D kernel using a sigma
    float sigma = 7.0;
    for (int j = 0; j <= kernelSize; ++j)
    {
    kernel[kernelSize+j] = kernel[kernelSize-j] = normalProbabilityDensityFunction(float(j), sigma);
    }

    // Generate the normalization factor
    float normalizationFactor = 0.0;
    for (int j = 0; j < matrixSize; ++j)
    {
    normalizationFactor += kernel[j];
    }
    normalizationFactor = normalizationFactor * normalizationFactor;

    // Apply the kernel to the fragment
    vec4 outputColor = vec4(0.0);
    for (int i=-kernelSize; i <= kernelSize; ++i)
    {
    for (int j=-kernelSize; j <= kernelSize; ++j)
    {
    float kernelValue = kernel[kernelSize+j]*kernel[kernelSize+i];
    vec2 sampleLocation = vecUV.xy + vec2(float(i)*offset.x,float(j)*offset.y);
    vec4 sample = texture2D(inputImageTexture, sampleLocation);
    outputColor += kernelValue * sample;
    }
    }

    // Divide by the normalization factor, so the weights sum to 1
    outputColor = outputColor/(normalizationFactor*normalizationFactor);

    return outputColor;
    }


    void main()
    {
    // After blurring, what alpha threshold should we define as outline?
    float alphaTreshold = 0.3;

    // How smooth the edges of the outline it should have?
    float outlineSmoothness = 0.1;

    // The outline color
    vec4 outlineColor = vec4(1.0, 1.0, 1.0, 1.0);

    // Sample the original image and generate a blurred version using a gaussian blur
    vec4 originalImage = texture2D(inputImageTexture, vecUV);
    vec4 blurredImage = gaussianBlur();


    float alpha = smoothstep(alphaTreshold - outlineSmoothness, alphaTreshold + outlineSmoothness, blurredImage.a);
    vec4 outlineFragmentColor = mix(vec4(0.0), outlineColor, alpha);

    gl_FragColor = mix(outlineFragmentColor, originalImage, originalImage.a);
    }
    这是我得到的结果:
    Result from outline algorithm.
    对于与您相同的图像,使用 matrixSize = 33 , alphaTreshold = 0.05 Result from outline algorithm with question image.
    为了获得更清晰的结果,我们可以调整参数。这是 matrixSize = 111 的示例, alphaTreshold = 0.05 , offset = vec2(0.002, 0.002) , alphaTreshold = 0.01 , 轮廓平滑度 = 0.00。注意增加 matrixSize将严重影响性能,这是仅使用一个着色器 channel 渲染此轮廓的限制。
    Result from outline algorithm with question image and smoother edges.
    我在 this site 上测试了着色器.希望您能够使其适应您的解决方案。
    关于引用文献,我用过不少 this shadertoy example作为我为此答案编写的代码的基础。

    关于android - 如何在 OpenGL 中使用 fragment 着色器制作更平滑的边框?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69481402/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com