2013-01-16 11 views
9

Tôi đang sử dụng WebGL để định lại kích thước hình ảnh của khách hàng rất nhanh chóng trong ứng dụng tôi đang thực hiện. Tôi đã viết một bóng đổ GLSL thực hiện lọc song tuyến đơn giản trên các hình ảnh mà tôi đang giảm kích thước.Tôi làm cách nào để cải thiện trình đổ bóng ảnh xuống WebGL/GLSL này

Nó hoạt động tốt cho hầu hết các phần nhưng có nhiều trường hợp thay đổi kích thước rất lớn, ví dụ: từ hình ảnh 2048x2048 xuống 110x110 để tạo hình thu nhỏ. Trong những trường hợp này chất lượng kém và quá mờ.

GLSL đổ bóng hiện tại của tôi là như sau:

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 và TexelsizeY chỉ đơn giản là (1.0/texture width) và chiều cao tương ứng ...

Tôi muốn thực hiện một kỹ thuật lọc chất lượng cao , lý tưởng là một bộ lọc [Lancosz] [1] sẽ tạo ra kết quả tốt hơn nhiều nhưng tôi dường như không thể hiểu được cách triển khai thuật toán với GLSL vì tôi rất mới đối với WebGL và GLSL nói chung.

Ai có thể chỉ cho tôi đúng hướng không?

Xin cảm ơn trước.

Trả lời

15

Nếu bạn đang tìm kiếm Lanczos resampling, sau đây là chương trình shader tôi sử dụng trong mở thư viện nguồn GPUImage tôi:

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

Điều này được áp dụng trong hai lần truyền, với lần đầu tiên thực hiện theo kiểu nằm ngang nằm ngang và lần lấy mẫu dọc thứ hai. Đồng phục texelWidthOffsettexelHeightOffset luân phiên được đặt thành 0.0 và phần phân đoạn chiều rộng hoặc chiều cao của một pixel trong hình ảnh.

Tôi tính toán sai lệch số mũ texel trong trình đổ bóng đỉnh vì điều này tránh kết cấu phụ thuộc đọc trên thiết bị di động mà tôi đang nhắm mục tiêu với điều này, dẫn đến hiệu suất tốt hơn đáng kể ở đó. Đó là một chút tiết, mặc dù.

Kết quả từ resampling Lanczos này:

Lanczos

Bình thường Bilinear downsampling:

Bilinear

gần hàng xóm downsampling:

Nearest-neighbor

+0

Một câu trả lời được xây dựng đẹp mắt. Cảm ơn bạn. Tôi sẽ có thể đạt được điều đó từ mã bạn đã đăng. Chúc mừng! – gordyr

+0

Chỉ để cho bạn biết tôi biết điều này làm việc một cách hoàn hảo và kết quả thật đẹp. Kỳ lạ là tôi phải đặt Texeloffsets (1.0/(destinationwidth * 3)) và (1.0/(destinationheight * 3)) để có kết quả tốt nhất. Tôi không chắc tôi hiểu tại sao nhưng sử dụng chiều rộng/chiều cao tiêu chuẩn tạo ra một hình ảnh rất mờ. Bất kể nó là tuyệt vời bây giờ. Lời cảm ơn to lớn! – gordyr

+0

@gordyr - Tốt để nghe. Bạn có nghĩa là bạn cần sử dụng texelWidthOffset = 3.0/(chiều rộng hình ảnh tính bằng pixel) hoặc texelWidthOffset = 1.0/(3.0 * (chiều rộng hình ảnh bằng pixel))?Tôi tạo ra những hình ảnh trên với texelWidthOffset = 1.0/(chiều rộng hình ảnh bằng pixel) và texelHeightOffset = 1.0/(chiều cao hình ảnh tính bằng pixel), nhưng nếu nhân tố của ba công trình cho bạn, hãy đi với nó. –