php - 在 PHP 中应用 FXAA?

标签 php glsl antialiasing php-gd fxaa

当我使用 PHP 的 GD 图像库来绘制形状时,它总是显示硬边缘。我尝试过使用 GD 的 imageantialias() 函数,但这仅适用于直线。

为了解决这个问题,我搜索了一些抗锯齿算法,发现FXAA效果很好,所以我打算尝试一下。我尝试从 GLSL 着色器移植 FXAA 抗锯齿滤镜 here .

然后,当我将 FXAA 着色器移植到 PHP 后,它没有给出正确的结果。我使用 PHP.net 上的 imagecolorallocatealpha() 示例测试 FXAA 过滤器:

<?php
require('fxaa.php');
$size = 300;
$image=imagecreatetruecolor($size, $size);

// something to get a white background with black border
$back = imagecolorallocate($image, 255, 255, 255);
$border = imagecolorallocate($image, 0, 0, 0);
imagefilledrectangle($image, 0, 0, $size - 1, $size - 1, $back);
imagerectangle($image, 0, 0, $size - 1, $size - 1, $border);

$yellow_x = 100;
$yellow_y = 75;
$red_x    = 120;
$red_y    = 165;
$blue_x   = 187;
$blue_y   = 125;
$radius   = 150;

// allocate colors with alpha values
$yellow = imagecolorallocatealpha($image, 255, 255, 0, 75);
$red    = imagecolorallocatealpha($image, 255, 0, 0, 75);
$blue   = imagecolorallocatealpha($image, 0, 0, 255, 75);

// drawing 3 overlapped circle
imagefilledellipse($image, $yellow_x, $yellow_y, $radius, $radius, $yellow);
imagefilledellipse($image, $red_x, $red_y, $radius, $radius, $red);
imagefilledellipse($image, $blue_x, $blue_y, $radius, $radius, $blue);
FXAA::process($image);

// don't forget to output a correct header!
header('Content-Type: image/png');

// and finally, output the result
imagepng($image);
imagedestroy($image);
?>

这是原始图像:

Original output

这是处理后的图像:

Final result

这是另一张测试图像。 (左侧经过 FXAA 处理,右侧未经过 FXAA 处理。)

Example image 2

示例图像的颜色与背景困惑,边缘过于平滑。这不是我想象的预期结果。我不明白我的代码出了什么问题,所以我寻求您的帮助。

此外,这是我编写的 FXAA 类和原始 GLSL 着色器:

<?php

class FXAA {
    const FXAA_REDUCE_MIN = 0.0078125;
    const FXAA_REDUCE_MUL = 0.125;
    const FXAA_SPAN_MAX = 8;

    static $w = 0;
    static $h = 0;

    private static function add($a, $b){
        return array($a[0] + $b[0], $a[1] + $b[1], $a[2] + $b[2]);
    }

    private static function dot($a, $b){
        return $a[0] * $b[0] + $a[1] * $b[1] + $a[2] * $b[2];
    }

    private static function texture2D($img, $pos){
        if(($pos[0] >= self::$w || $pos[0] < 0) || ($pos[1] >= self::$h || $pos[1] < 0)){
            return array(0, 0, 0, 0);
        }
        $color = imagecolorat($img, $pos[0], $pos[1]);
        $a = ($color >> 24) & 0xFF; 
        $r = ($color >> 16) & 0xFF; 
        $g = ($color >> 8) & 0xFF; 
        $b = $color & 0xFF; 
        return array($r, $g, $b, $a);
    }

    public static function process($img){
        self::$w = imagesx($img);
        self::$h = imagesy($img);

        for($x = 0; $x < imagesx($img); $x++){
            for($y = 0; $y < imagesy($img); $y++){
                $rgbNW = self::texture2D($img,array($x - 1, $y - 1));
                $rgbNE = self::texture2D($img,array($x + 1, $y - 1));
                $rgbSW = self::texture2D($img,array($x - 1, $y + 1));
                $rgbSE = self::texture2D($img,array($x + 1, $y + 1));
                $rgbaM = $rgbM = self::texture2D($img,array($x, $y));
                $opacity = array_pop($rgbM);

                $luma = array(76, 149, 29);

                $lumaNW = self::dot($rgbNW, $luma);
                $lumaNE = self::dot($rgbNE, $luma);
                $lumaSW = self::dot($rgbSW, $luma);
                $lumaSE = self::dot($rgbSE, $luma);
                $lumaM = self::dot($rgbM, $luma);
                $lumaMin = min($lumaM, min(min($lumaNW, $lumaNE ), min($lumaSW, $lumaSE)));
                $lumaMax = max($lumaM, max(max($lumaNW, $lumaNE), max($lumaSW, $lumaSE)));

                $dir = array(
                    -(($lumaNW + $lumaNE) - ($lumaSW + $lumaSE)),
                    (($lumaNW + $lumaSW) - ($lumaNE + $lumaSE))
                );
                $dirReduce = max(($lumaNW + $lumaNE + $lumaSW + $lumaSE ) * ( 0.25 * self::FXAA_REDUCE_MUL ), self::FXAA_REDUCE_MIN);
                $rcpDirMin = 1 / (min(abs($dir[0]), abs($dir[1])) + $dirReduce);

                $dir[0] = min(self::FXAA_SPAN_MAX, max(-self::FXAA_SPAN_MAX, $dir[0] * $rcpDirMin));
                $dir[1] = min(self::FXAA_SPAN_MAX, max(-self::FXAA_SPAN_MAX, $dir[1] * $rcpDirMin));

                $rgbA = self::add(
                    self::texture2D($img, array($x + $dir[0] * (1 / 3 - 0.5), $y + $dir[1] * (1 / 3 - 0.5))),
                    self::texture2D($img, array($x + $dir[0] * (2 / 3 - 0.5), $y + $dir[1] * (1 / 3 - 0.5)))
                );
                $rgbA[0] *= 0.5;
                $rgbA[1] *= 0.5;
                $rgbA[2] *= 0.5;

                $rgbB = self::add(
                    self::texture2D($img, array($x + $dir[0] * -0.5, $y + $dir[1] * -0.5)),
                    self::texture2D($img, array($x + $dir[0] * 0.5, $y + $dir[1] * 0.5))
                );
                $rgbB[0] = $rgbB[0] * 0.25 + $rgbA[0] * 0.5;
                $rgbB[1] = $rgbB[1] * 0.25 + $rgbA[1] * 0.5;
                $rgbB[2] = $rgbB[2] * 0.25 + $rgbA[2] * 0.5;

                $lumaB = self::dot($rgbB, $luma);

                if(($lumaB < $lumaMin) || ($lumaB > $lumaMax)){
                    imagesetpixel($img, $x, $y, imagecolorallocatealpha($img, $rgbA[0], $rgbA[1], $rgbA[2], $opacity));
                }
                else {
                    imagesetpixel($img, $x, $y, imagecolorallocatealpha($img, $rgbB[0], $rgbB[1], $rgbB[2], $opacity));
                }
            }
        }
    }
}

原始 GLSL 着色器:

uniform sampler2D tDiffuse;
uniform vec2 resolution;
varying vec2 vUv;

#define FXAA_REDUCE_MIN   (1.0/128.0)
#define FXAA_REDUCE_MUL   (1.0/8.0)
#define FXAA_SPAN_MAX     8.0

void main() {
    vec3 rgbNW = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ) * resolution ).xyz;
    vec3 rgbNE = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ) * resolution ).xyz;
    vec3 rgbSW = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ) * resolution ).xyz;
    vec3 rgbSE = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ) * resolution ).xyz;

    vec4 rgbaM  = texture2D( tDiffuse,  gl_FragCoord.xy  * resolution );
    vec3 rgbM  = rgbaM.xyz;
    float opacity  = rgbaM.w;

    vec3 luma = vec3( 0.299, 0.587, 0.114 );
    float lumaNW = dot( rgbNW, luma );
    float lumaNE = dot( rgbNE, luma );
    float lumaSW = dot( rgbSW, luma );
    float lumaSE = dot( rgbSE, luma );
    float lumaM  = dot( rgbM,  luma );
    float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) );
    float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) );

    vec2 dir;
    dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));
    dir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));
    float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );
    float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );
    dir = min( vec2( FXAA_SPAN_MAX,  FXAA_SPAN_MAX),max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),dir * rcpDirMin)) * resolution;

    vec3 rgbA = 0.5 * (
        texture2D( tDiffuse, gl_FragCoord.xy  * resolution + dir * ( 1.0 / 3.0 - 0.5 ) ).xyz +
        texture2D( tDiffuse, gl_FragCoord.xy  * resolution + dir * ( 2.0 / 3.0 - 0.5 ) ).xyz );

    vec3 rgbB = rgbA * 0.5 + 0.25 * (
        texture2D( tDiffuse, gl_FragCoord.xy  * resolution + dir * -0.5 ).xyz +
        texture2D( tDiffuse, gl_FragCoord.xy  * resolution + dir * 0.5 ).xyz );
    float lumaB = dot( rgbB, luma );
    if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) ) {
        gl_FragColor = vec4( rgbA, opacity );
    } else {
        gl_FragColor = vec4( rgbB, opacity );
    }
}

最佳答案

您的代码的问题在于它在单个图像上运行,因此抗锯齿操作读取其自己的输出。

将抗锯齿操作的输出绘制到新图像中即可。

关于php - 在 PHP 中应用 FXAA?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17739697/

相关文章:

php - 如何在html中插入php?

php - 无法登录 - 并且没有错误消息。 - PHP

opengl - 使用 openGL 着色器 (glsl) 在 LibGDX 中将纹理内部边框淡化为透明

opencv - 如何将视频(帧)输入 GLSL 着色器

c++ - 在 Qt 应用程序中禁用文本抗锯齿

android - Android 中的大文本

php - 如何在导出的xml中添加DOCTYPE?

php - 在 phpmyadmin 中工作的 sql 查询 - 在 php 中不工作

opengl - 有没有超过 GL_MAX_VIEWPORTS 的有效方法?

android - Android OpenGL 多重采样/抗锯齿问题