4

我在 OpenGL 中追求基于平滑纹理的轮廓效果。到目前为止,我尝试了几乎所有类型的边缘检测算法,这些算法主要导致粗糙和锯齿状的轮廓。然后我读到了距离场。我找到了一个非常好的距离场示例。这是 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();
}

此着色器的结果使用漫反射颜色覆盖整个屏幕,用于填充距离场轮廓之外的屏幕区域。如下所示: 在此处输入图像描述

我需要的是让距离场外所有具有纯红色填充的区域保持透明。

4

2 回答 2

2

我通过使用距离场灰度 8 位 alpha 贴图得出了解决方案。Stefan Gustavson 详细描述了如何做到这一点。基本上需要生成原始纹理的距离场版本。然后这个纹理通常在第一遍中使用图元渲染到 FBO。在第二遍中,alpha 混合模式应该开启。第一次传递的纹理与屏幕四边形一起使用。在这个阶段,片段着色器从该纹理中采样 alpha。这会产生平滑的边缘和边缘周围的 alpha 透明度。结果如下: 在此处输入图像描述

于 2012-09-03T06:38:36.357 回答
0

根据屏幕截图,我假设您正在渲染全屏四边形?如果是这种情况,蒂姆刚刚提供了答案,请尝试:

glEnable( GL_BLEND );
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 

在渲染四边形之前。显然,如果您也要渲染不透明的东西,我建议您先渲染它们,这样您就不会遇到深度缓冲区问题。绘制完透明的东西后,调用:

glDisable( GL_BLEND ); 

再次关闭 alphablending。

于 2012-09-01T20:13:14.530 回答