2013-01-16 11 views
9

WebGLを使用して、作業中のアプリケーション内で非常に迅速に画像クライアントのサイズを変更しています。私は、縮小している画像に対して簡単な双線形フィルタリングを実行するGLSLシェーダを作成しました。このWebGL/GLSL画像のダウンサンプリングシェーダを改善するには

大部分はうまく動作しますが、サイズ変更が大規模な場合など、多くの場合があります。サムネイルを生成するために、2048x2048の画像から110x110に縮小します。これらの例では、品質は貧弱で、ぼやけています。私は、高品質のフィルタリング技術を実装したいと思います

uniform float textureSizeWidth;\ 
uniform float textureSizeHeight;\ 
uniform float texelSizeX;\ 
uniform float texelSizeY;\ 
varying mediump vec2 texCoord;\ 
uniform sampler2D texture;\ 
\ 
vec4 tex2DBiLinear(sampler2D textureSampler_i, vec2 texCoord_i)\ 
{\ 
    vec4 p0q0 = texture2D(textureSampler_i, texCoord_i);\ 
    vec4 p1q0 = texture2D(textureSampler_i, texCoord_i + vec2(texelSizeX, 0));\ 
\ 
    vec4 p0q1 = texture2D(textureSampler_i, texCoord_i + vec2(0, texelSizeY));\ 
    vec4 p1q1 = texture2D(textureSampler_i, texCoord_i + vec2(texelSizeX , texelSizeY));\ 
\ 
    float a = fract(texCoord_i.x * textureSizeWidth);\ 
\ 
    vec4 pInterp_q0 = mix(p0q0, p1q0, a);\ 
    vec4 pInterp_q1 = mix(p0q1, p1q1, a);\ 
\ 
    float b = fract(texCoord_i.y * textureSizeHeight);\ 
    return mix(pInterp_q0, pInterp_q1, b);\ 
}\ 
void main() { \ 
\ 
    gl_FragColor = tex2DBiLinear(texture,texCoord);\ 
}'); 

TexelsizeXとTexelsizeYは、(1.0 /テクスチャ幅)だけであり、それぞれ高さ...

次のように

私の現在のGLSLシェーダがある 理想的にははるかに良い結果を生むはずの[Lancosz] [1]フィルタですが、WebGLとGLSLの一般的な新機能として、GLSLを使ってアルゴリズムを実装する方法について頭を下げているようです。

誰かが正しい方向に向いていますか?

ありがとうございます。

答えて

15

あなたはランチョスリサンプリングを探しているなら、次は私が私のオープンソースGPUImageライブラリで使用シェーダープログラムである:

頂点シェーダ:

attribute vec4 position; 
attribute vec2 inputTextureCoordinate; 

uniform float texelWidthOffset; 
uniform float texelHeightOffset; 

varying vec2 centerTextureCoordinate; 
varying vec2 oneStepLeftTextureCoordinate; 
varying vec2 twoStepsLeftTextureCoordinate; 
varying vec2 threeStepsLeftTextureCoordinate; 
varying vec2 fourStepsLeftTextureCoordinate; 
varying vec2 oneStepRightTextureCoordinate; 
varying vec2 twoStepsRightTextureCoordinate; 
varying vec2 threeStepsRightTextureCoordinate; 
varying vec2 fourStepsRightTextureCoordinate; 

void main() 
{ 
    gl_Position = position; 

    vec2 firstOffset = vec2(texelWidthOffset, texelHeightOffset); 
    vec2 secondOffset = vec2(2.0 * texelWidthOffset, 2.0 * texelHeightOffset); 
    vec2 thirdOffset = vec2(3.0 * texelWidthOffset, 3.0 * texelHeightOffset); 
    vec2 fourthOffset = vec2(4.0 * texelWidthOffset, 4.0 * texelHeightOffset); 

    centerTextureCoordinate = inputTextureCoordinate; 
    oneStepLeftTextureCoordinate = inputTextureCoordinate - firstOffset; 
    twoStepsLeftTextureCoordinate = inputTextureCoordinate - secondOffset; 
    threeStepsLeftTextureCoordinate = inputTextureCoordinate - thirdOffset; 
    fourStepsLeftTextureCoordinate = inputTextureCoordinate - fourthOffset; 
    oneStepRightTextureCoordinate = inputTextureCoordinate + firstOffset; 
    twoStepsRightTextureCoordinate = inputTextureCoordinate + secondOffset; 
    threeStepsRightTextureCoordinate = inputTextureCoordinate + thirdOffset; 
    fourStepsRightTextureCoordinate = inputTextureCoordinate + fourthOffset; 
} 

フラグメントシェーダ:

precision highp float; 

uniform sampler2D inputImageTexture; 

varying vec2 centerTextureCoordinate; 
varying vec2 oneStepLeftTextureCoordinate; 
varying vec2 twoStepsLeftTextureCoordinate; 
varying vec2 threeStepsLeftTextureCoordinate; 
varying vec2 fourStepsLeftTextureCoordinate; 
varying vec2 oneStepRightTextureCoordinate; 
varying vec2 twoStepsRightTextureCoordinate; 
varying vec2 threeStepsRightTextureCoordinate; 
varying vec2 fourStepsRightTextureCoordinate; 

// sinc(x) * sinc(x/a) = (a * sin(pi * x) * sin(pi * x/a))/(pi^2 * x^2) 
// Assuming a Lanczos constant of 2.0, and scaling values to max out at x = +/- 1.5 

void main() 
{ 
    lowp vec4 fragmentColor = texture2D(inputImageTexture, centerTextureCoordinate) * 0.38026; 

    fragmentColor += texture2D(inputImageTexture, oneStepLeftTextureCoordinate) * 0.27667; 
    fragmentColor += texture2D(inputImageTexture, oneStepRightTextureCoordinate) * 0.27667; 

    fragmentColor += texture2D(inputImageTexture, twoStepsLeftTextureCoordinate) * 0.08074; 
    fragmentColor += texture2D(inputImageTexture, twoStepsRightTextureCoordinate) * 0.08074; 

    fragmentColor += texture2D(inputImageTexture, threeStepsLeftTextureCoordinate) * -0.02612; 
    fragmentColor += texture2D(inputImageTexture, threeStepsRightTextureCoordinate) * -0.02612; 

    fragmentColor += texture2D(inputImageTexture, fourStepsLeftTextureCoordinate) * -0.02143; 
    fragmentColor += texture2D(inputImageTexture, fourStepsRightTextureCoordinate) * -0.02143; 

    gl_FragColor = fragmentColor; 
} 

これは2回のパスで適用され、最初は水平方向のダウンサンプリングを実行し、2回目は垂直方向のダウンサンプリングを行います。 texelWidthOffsettexelHeightOffsetユニフォームは、0.0と、画像内の1つのピクセルの幅の割合または高さの割合に交互に設定されます。

私は頂点シェーダのテクセルオフセットをハード計算します。これは、これで目標とするモバイルデバイス上の依存テクスチャの読み込みを避けるため、そこでのパフォーマンスが大幅に向上します。しかし、少し冗長です。このランチョスリサンプリングから

結果:

Lanczos

通常のバイリニアダウンサンプリング:

Bilinear

最近傍ダウンサンプリング:

Nearest-neighbor

+0

A美しく建設された答え。ありがとうございました。あなたが投稿したコードからそこにアクセスできるはずです。乾杯! – gordyr

+0

私は知っているだけ知っているこれは完全に動作していると結果は美しいです。不思議なことに、最良の結果を得るためにTexeloffsetを(1.0 /(destinationwidth * 3))と(1.0 /(destinationheight * 3))に設定しなければなりませんでした。私はなぜ、標準の幅/高さを使用するのが非常にぼやけた画像を作り出したのか分かりません。それにもかかわらず、今は素晴らしいです。巨大な感謝! – gordyr

+0

@ gordyr - 聞いてよかったです。あなたは、texelWidthOffset = 3.0 /(イメージのピクセル単位のピクセル)またはtexelWidthOffset = 1.0 /(3.0 *(ピクセル単位のイメージの幅))を使用する必要があることを意味しますか?私は上記の画像をtexelWidthOffset = 1.0 /(画像の幅はピクセル)、texelHeightOffset = 1.0 /(画像の高さはピクセル)で生成しましたが、3倍の要素があればそれに合わせてください。 –

関連する問題