2013-01-16 15 views
9

Sto utilizzando WebGL per ridimensionare le immagini del client molto velocemente all'interno di un'app su cui sto lavorando. Ho scritto uno shader GLSL che esegue un semplice filtro bilineare sulle immagini che sto ridimensionando.Come migliorare questo shader di downsampling di immagini WebGL/GLSL

Funziona bene per la maggior parte, ma ci sono molte occasioni in cui il ridimensionamento è enorme per es. da un'immagine 2048x2048 fino a 110x110 per generare una miniatura. In questi casi la qualità è scarsa e troppo sfocata.

mio attuale Shader GLSL è la seguente:

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 e TexelsizeY sono semplicemente (1.0/larghezza texture) e l'altezza rispettivamente ...

Vorrei implementare una tecnica di filtraggio qualità superiore , idealmente un filtro [Lancosz] [1] che dovrebbe produrre risultati molto migliori ma non riesco a capire come implementare l'algoritmo con GLSL poiché sono molto nuovo a WebGL e GLSL in generale.

Qualcuno potrebbe indicarmi la giusta direzione?

Grazie in anticipo.

risposta

15

Se siete alla ricerca di Lanczos ricampionamento, il seguente è il programma di shader che uso nel mio aperta libreria di origine GPUImage:

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

Questo viene applicato in due passaggi, con il primo eseguendo un downsampling orizzontale e il secondo un downsampling verticale. Le uniformi texelWidthOffset e texelHeightOffset sono alternativamente impostate su 0.0 e la frazione di larghezza o frazione di altezza di un singolo pixel nell'immagine.

È possibile calcolare con precisione gli offset texel nel vertex shader in quanto evita letture dipendenti delle texture sui dispositivi mobili con cui mi sto rivolgendo, ottenendo prestazioni significativamente migliori. È un po 'prolisso, però.

I risultati di questo ricampionamento Lanczos:

Lanczos

Normale bilineare downsampling:

Bilinear

primi vicini downsampling:

Nearest-neighbor

+0

A risposta splendidamente costruita. Grazie. Dovrei essere in grado di arrivarci dal codice che hai postato. Saluti! – gordyr

+0

Solo per farti sapere, so che questo funziona perfettamente ed i risultati sono belli. Stranamente ho dovuto impostare gli offset di Texel su (1.0/(destinationwidth * 3)) e (1.0/(destinationheight * 3)) per i migliori risultati. Non sono sicuro di aver capito perché, ma utilizzando la larghezza/altezza standard abbiamo prodotto un'immagine molto sfocata. Indipendentemente da ciò è favoloso ora. Enorme grazie! – gordyr

+0

@gordyr - Buono a sapersi. Vuoi dire che dovevi usare texelWidthOffset = 3.0/(larghezza dell'immagine in pixel) o texelWidthOffset = 1.0/(3.0 * (larghezza dell'immagine in pixel))?Ho generato le immagini di cui sopra con texelWidthOffset = 1.0/(larghezza dell'immagine in pixel) e texelHeightOffset = 1.0/(altezza dell'immagine in pixel), ma se un fattore tre funziona per te, vai con esso. –

Problemi correlati