2013-01-16 12 views
9

Ich benutze WebGL, um Bilder sehr schnell innerhalb einer App, an der ich gerade arbeite, clientseitig zu skalieren. Ich habe einen GLSL-Shader geschrieben, der eine einfache bilineare Filterung der Bilder durchführt, die ich verkleinere.Wie kann ich diesen WebGL/GLSL-Bild-Downsampling-Shader verbessern?

Es funktioniert in den meisten Fällen gut, aber es gibt viele Gelegenheiten, wo die Größe ist groß, z. von einem 2048x2048 Bild bis zu 110x110 um ein Thumbnail zu erzeugen. In diesen Fällen ist die Qualität schlecht und viel zu verschwommen.

Mein aktueller GLSL Shader ist wie folgt:

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 und TexelsizeY sind einfach (1,0/Textur Breite) und Höhe jeweils ...

Ich möchte eine höhere Qualität Filtertechnik implementieren , idealerweise ein [Lancosz] [1] -Filter, der weitaus bessere Ergebnisse liefern sollte, aber ich kann mich nicht damit abfinden, wie man den Algorithmus mit GLSL implementiert, da ich für WebGL und GLSL im Allgemeinen sehr neu bin.

Kann mir jemand in die richtige Richtung zeigen?

Vielen Dank im Voraus.

Antwort

15

Wenn Sie sich für Lanczos-Filter suchen, ist das folgende das Shader-Programm ich in meiner Open-Source-GPUImage Bibliothek verwenden:

Vertex-Shader:

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; 
} 

Fragment-Shader:

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; 
} 

Dies wird in zwei Durchläufen angewendet, wobei das erste ein horizontales Downsampling und das zweite ein vertikales Downsampling durchführt. Die texelWidthOffset und texelHeightOffset Uniformen werden abwechselnd auf 0,0 und den Breitenbruch oder den Höhenbruchteil eines einzelnen Pixels in dem Bild gesetzt.

Ich errechne die Texel-Offsets im Vertex-Shader, weil dadurch auf den Mobilgeräten, die ich anvisiere, keine Texturlesevorgänge mehr möglich sind, was zu einer deutlich besseren Leistung führt. Es ist jedoch ein wenig wortreich.

Die Ergebnisse dieser Lanczos-Filter:

Lanczos

Normale bilinear Downsampling:

Bilinear

Nächster-Nachbar-Abwärtsabtastens:

Nearest-neighbor

+0

A schön konstruierte Antwort. Vielen Dank. Ich sollte aus dem Code kommen, den du gepostet hast. Prost! – gordyr

+0

Nur um Sie wissen zu lassen, weiß ich, dass dies perfekt funktioniert und die Ergebnisse sind wunderschön. Seltsamerweise musste ich die Texeloffsets auf (1.0/(Zielbreite * 3)) und (1.0/(Zielhöhe * 3)) setzen, um die besten Ergebnisse zu erzielen. Ich bin mir nicht sicher, warum ich das verstehe, aber die Verwendung der Standardbreite/-höhe erzeugte ein sehr verschwommenes Bild. Egal, es ist jetzt fabelhaft. Vielen Dank! – gordyr

+0

@gordyr - Gut zu hören. Sie meinen, dass Sie texelWidthOffset = 3.0/(Bildbreite in Pixel) oder texelWidthOffset = 1.0/(3.0 * (Bildbreite in Pixel)) benötigen?Ich habe die obigen Bilder mit texelWidthOffset = 1.0/(Bildbreite in Pixeln) und texelHeightOffset = 1.0/(Bildhöhe in Pixeln) erzeugt, aber wenn ein Faktor von drei für Sie funktioniert, gehen Sie mit. –

Verwandte Themen