我是在OpenGL中基于平滑纹理的轮廓效果.到目前为止,我尝试了大多数各种边缘检测算法,这些算法主要是粗糙和锯齿状的轮廓.然后我读了大约
Distance Field.我找到了一个非常好的距离场的例子.这是GLSL代码:
#version 420
layout(binding=0) uniform sampler2D colorMap;
flat in vec4 diffuseOut;
in vec2 uvsOut;
out vec4 outputColor;
const float ALPHA_THRESHOLD = 0.9;
const float NUM_SPOKES = 36.0; // Number of radiating lines to check in.
const float ANGULAR_STEP =360.0 / NUM_SPOKES;
const int ZERO_VALUE =128; // Color channel containing 0 => -128, 128 => 0, 255 => +127
int in_StepSize=15; // Distance to check each time (larger steps will be faster, but less accurate).
int in_MaxDistance=30; // Maximum distance to search out to. Cannot be more than 127!
vec4 distField(){
vec2 pixel_size = 1.0 / vec2(textureSize(colorMap, 0));
vec2 screenTexCoords = gl_FragCoord.xy * pixel_size;
int distance;
if(texture(colorMap, screenTexCoords).a == 0.0)
{
// Texel is transparent, search for nearest opaque.
distance = ZERO_VALUE + 1;
for(int i = in_StepSize; i < in_MaxDistance; i += in_StepSize)
{
if(find_alpha_at_distance(screenTexCoords, float(i) * pixel_size, 1.0))
{
i = in_MaxDistance + 1; // BREAK!
}
else
{
distance = ZERO_VALUE + 1 + i;
}
}
}
else
{
// Texel is opaque, search for nearest transparent.
distance = ZERO_VALUE;
for(int i = in_StepSize; i <= in_MaxDistance; i += in_StepSize)
{
if(find_alpha_at_distance(screenTexCoords, float(i) * pixel_size, 0.0))
{
i = in_MaxDistance + 1; // BREAK!
}
else
{
distance = ZERO_VALUE - i;
}
}
}
return vec4(vec3(float(distance) / 255.0) * diffuseOut.rgb, 1.0 - texture(colorMap, screenTexCoords).a);
}
void main()
{
outputColor= distField();
}
此着色器的结果覆盖整个屏幕,使用漫反射颜色填充距离场轮廓之外的屏幕区域.这是它的样子:
我需要的是将所有在距离场外具有红色实心填充的区域保持透明.
最佳答案 我通过使用距离场灰度8位alpha map.
Stefan Gustavson来解决这个问题
详细描述了如何做.基本上需要生成原始纹理的距离场版本.然后通常在第一次传递到FBO时使用图元渲染此纹理.在第二次传递中,alpha混合模式应该打开来自第一遍的纹理与屏幕四边形一起使用.在此阶段,片段着色器从该纹理中采样alpha.这导致边缘周围的平滑边缘和alpha透明度.
结果如下: