Mih*_*hah 5 android opengl-es glsl
我一直在尝试在 Android 中使用 OpenGL 绘制具有透明背景的图像的边框。我正在使用片段着色器和顶点着色器。(来自 GPUImage 库)
下面我添加了图A和图B。
图A。
图B。
我已经实现了图A。使用定制的片段着色器。但无法使边框变得更平滑,如图B所示。我附上我使用过的着色器代码(以实现粗糙边框)。这里有人可以帮助我如何使边界更平滑吗?
这是我的顶点着色器:
attribute vec4 position;
attribute vec4 inputTextureCoordinate;
varying vec2 textureCoordinate;
void main()
{
gl_Position = position;
textureCoordinate = inputTextureCoordinate.xy;
}
Run Code Online (Sandbox Code Playgroud)
这是我的片段着色器:
我已经计算出当前像素周围的 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;
}
Run Code Online (Sandbox Code Playgroud)
你几乎走上了正轨。
主要算法是:
主要问题是模糊步骤。它需要是大而平滑的模糊才能获得您想要的平滑轮廓。对于模糊,我们可以使用卷积滤波器Kernel。为了实现大模糊,我们应该使用大内核。我建议使用高斯模糊分布,因为它是众所周知和使用的。
该算法的概述是:
对于每个片段,我们对其周围的许多位置进行采样。样本是在 N × N 网格中制作的。我们使用遵循2D 高斯分布的权重对它们进行平均。这会导致图像模糊。
对于模糊图像,我们用轮廓颜色绘制 alpha 大于阈值的片段。当然,原始图像中的任何不透明像素也应该出现在结果中。
顺便说一句,您的解决方案几乎是一个模糊的 3 x 3 内核(您在 3 x 3 网格中对片段周围的位置进行采样)。但是,3 x 3 内核无法提供所需的模糊量。您需要更多样本(例如 11 x 11)。此外,靠近中心的权重对结果的影响应该更大。因此,统一的权重效果不会很好。
哦,还有一件重要的事情:
一个着色器来完成这一点并不是实现这一点的最快方法。通常,这将通过两个单独的渲染来完成。第一个渲染将照常渲染图像,第二个渲染将模糊并添加轮廓。我假设您想通过 1 个渲染来完成此操作。
以下是实现此目的的顶点和片段着色器:
顶点着色器
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);
}
Run Code Online (Sandbox Code Playgroud)
片段着色器
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);
}
Run Code Online (Sandbox Code Playgroud)
这是我得到的结果:
对于与您的相同的图像matrixSize = 33,alphaTreshold = 0.05
为了获得更清晰的结果,我们可以调整参数。matrixSize = 111以下是、alphaTreshold = 0.05、offset = vec2(0.002, 0.002)、 、 轮廓平滑度 = 0.00的示例alphaTreshold = 0.01。请注意,增加matrixSize将严重影响性能,这是仅使用一个着色器通道渲染此轮廓的限制。
我在这个网站上测试了着色器。希望您能够使其适应您的解决方案。
关于参考,我使用了很多这个shadertoy示例作为我为此答案编写的代码的基础。
| 归档时间: |
|
| 查看次数: |
1867 次 |
| 最近记录: |