2010-05-09 7 views
1

Я следил за this tutorial с помощью Apple OpenGL Shader Builder (инструмент похож на Nvidia fx composer, но проще).Блочные фильтры с использованием фрагментарных шейдеров

Я мог бы легко применять фильтры, но я не понимаю, правильно ли они работали (и если да, то как я могу улучшить выход). Например, фильтр размытия: сам OpenGL выполняет некоторую обработку изображений на текстурах, поэтому, если они отображаются с более высоким разрешением, чем исходное изображение, они уже размыты OpenGL. Во-вторых, размытая часть ярче, чем часть, не обработанная, я думаю, что это не имеет смысла, поскольку она просто берет пиксели из прямого соседства. Это определяется как

float step_w = (1.0/width); 

Который я не совсем понимаю: пиксели индексируются с использованием значений с плавающей запятой ??

Blurred Image http://img218.imageshack.us/img218/6468/blurzt.png

Edit: Я забыл прикрепить точный код я использовал:

пиксельный шейдер

// Originally taken from: http://www.ozone3d.net/tutorials/image_filtering_p2.php#part_2 

#define KERNEL_SIZE 9 

float kernel[KERNEL_SIZE]; 

uniform sampler2D colorMap; 
uniform float width; 
uniform float height; 

float step_w = (1.0/width); 
float step_h = (1.0/height); 

// float step_w = 20.0; 
// float step_h = 20.0; 

vec2 offset[KERNEL_SIZE]; 

void main(void) 
{ 
    int i = 0; 
    vec4 sum = vec4(0.0); 

    offset[0] = vec2(-step_w, -step_h); // south west 
    offset[1] = vec2(0.0, -step_h);  // south 
    offset[2] = vec2(step_w, -step_h);  // south east 

    offset[3] = vec2(-step_w, 0.0);  // west 
    offset[4] = vec2(0.0, 0.0);   // center 
    offset[5] = vec2(step_w, 0.0);  // east 

    offset[6] = vec2(-step_w, step_h);  // north west 
    offset[7] = vec2(0.0, step_h);  // north 
    offset[8] = vec2(step_w, step_h);  // north east 


// Gaussian kernel 
// 1 2 1 
// 2 4 2 
// 1 2 1 


    kernel[0] = 1.0;  kernel[1] = 2.0; kernel[2] = 1.0; 
    kernel[3] = 2.0; kernel[4] = 4.0; kernel[5] = 2.0; 
    kernel[6] = 1.0;  kernel[7] = 2.0; kernel[8] = 1.0; 


// TODO make grayscale first 
// Laplacian Filter 
// 0 1 0 
// 1 -4 1 
// 0 1 0 

/* 
kernel[0] = 0.0; kernel[1] = 1.0; kernel[2] = 0.0; 
kernel[3] = 1.0; kernel[4] = -4.0; kernel[5] = 1.0; 
kernel[6] = 0.0; kernel[7] = 2.0; kernel[8] = 0.0; 
*/ 

// Mean Filter 
// 1 1 1 
// 1 1 1 
// 1 1 1 

/* 
kernel[0] = 1.0; kernel[1] = 1.0; kernel[2] = 1.0; 
kernel[3] = 1.0; kernel[4] = 1.0; kernel[5] = 1.0; 
kernel[6] = 1.0; kernel[7] = 1.0; kernel[8] = 1.0; 
*/ 

    if(gl_TexCoord[0].s<0.5) 
    { 
     // For every pixel sample the neighbor pixels and sum up 
     for(i=0; i<KERNEL_SIZE; i++) 
     { 
      // select the pixel with the concerning offset 
      vec4 tmp = texture2D(colorMap, gl_TexCoord[0].st + offset[i]); 
      sum += tmp * kernel[i]; 
     } 

     sum /= 16.0; 
    } 
    else if(gl_TexCoord[0].s>0.51) 
    { 
     sum = texture2D(colorMap, gl_TexCoord[0].xy); 
    } 
    else // Draw a red line 
    { 
     sum = vec4(1.0, 0.0, 0.0, 1.0); 
    } 

    gl_FragColor = sum; 
} 

Vertex Shader

void main(void) 
{ 
    gl_TexCoord[0] = gl_MultiTexCoord0; 
    gl_Position = ftransform(); 
} 
+0

что еще вопрос? координаты текстуры адресуются в диапазоне [0-1], да. – Bahbar

ответ

4

Координаты текстуры обычно достигают от (0,0) (внизу слева) до (1,1) (вверху справа), так что на самом деле они являются поплавками.

Так что если у вас есть texturecoordinates (u,v), то "оригинальные" координаты вычисляются (u*textureWidth, v*textureHeight).

Если полученные значения не являются целыми числами, могут существовать различные способы обработки, что:

  • просто взять floor или ceil результата для того, чтобы сделать номер интегральную
  • интерполировать между соседними texels

Однако я думаю, что каждый язык затенения имеет метод доступа к текстуре по их «оригинальному», т.е. интегральному индексу.

+0

Итак, step_w = (1.0/width) должно быть числовым расстоянием до следующего пикселя. – Nils

+0

yep, должно быть так. – phimuemue

+0

ugh. Этот ответ имеет много аппроксимаций. 1/нижнее левое и верхнее правое не слишком много для GL. Он не дает ориентации на текстуры. 2/нет таких вещей, как «оригинальные» координаты. Есть очень веские причины, по которым текстурные координаты идут от 0 до 1. Одним из них является mip-mapping. 3/texels адресуются в их середине ... – Bahbar

0

@Nils, спасибо, что разместили этот код. Я пытался найти простой способ сделать свертку на графическом процессоре в течение некоторого времени. Я попробовал свой код и сам столкнулся с той же проблемой затемнения. Вот как я это решил.

  • Вы должны быть осторожны с размером шага, чтобы использовать ширину текстуры не ширина изображения. Обычно он получает размер до 2, когда текстура связана с opengl.
  • Вы также должны обязательно нормализовать ядро ​​ , суммируя все значения в своем ядре и делясь на это.
  • Также это помогает, если вы свернете R G и B отдельно без освещения (четвертый компонент образца).

Вот решение, которое не имеет проблемы с затемнением, а также обходит необходимость в массиве смещений для ядер 3х3.

Я включил 8 ядер, которые работали для меня без затемнения.

uniform sampler2D colorMap; 
uniform float width; 
uniform float height; 


const mat3 SobelVert= mat3(1.0, 2.0, 1.0, 0.0, 0.0, 0.0, -1.0, -2.0, -1.0); 
const mat3 SobelHorz= mat3(1.0, 0.0, -1.0, 2.0, 0.0, -2.0, 1.0, 0.0, -1.0); 
const mat3 SimpleBlur= (1.0/9.0)*mat3(1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0); 
const mat3 Sharpen= mat3(0.0, -1.0, 0.0, -1.0, 5.0, -1.0, 0.0, -1.0, 0.0); 
const mat3 GaussianBlur= (1.0/16.0)*mat3(1.0, 2.0, 1.0, 2.0, 4.0, 2.0, 1.0, 2.0, 1.0); 
const mat3 SimpleHorzEdge= mat3(0.0, 0.0, 0.0, -3.0, 3.0, 0.0, 0.0, 0.0, 0.0); 
const mat3 SimpleVertEdge= mat3(0.0, -3.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0); 
const mat3 ClearNone= mat3(0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0); 

void main(void) 
{ 
    vec4 sum = vec4(0.0); 
    if(gl_TexCoord[0].x <0.5) 
    { 
     mat3 I, R, G, B; 
     vec3 sample; 

     // fetch the 3x3 neighbourhood and use the RGB vector's length as intensity value 
     for (int i=0; i<3; i++){ 
     for (int j=0; j<3; j++) { 
      sample = texture2D(colorMap, gl_TexCoord[0].xy + vec2(i-1,j-1)/vec2(width, height)).rgb; 
      I[i][j] = length(sample); //intensity (or illumination) 
      R[i][j] = sample.r; 
      G[i][j] = sample.g; 
      B[i][j] = sample.b; 
     } 
     } 

     //apply the kernel convolution 
     mat3 convolvedMatR = matrixCompMult(SimpleBlur, R); 
     mat3 convolvedMatG = matrixCompMult(SimpleBlur, G); 
     mat3 convolvedMatB = matrixCompMult(SimpleBlur, B); 
     float convR = 0.0; 
     float convG = 0.0; 
     float convB = 0.0; 
     //sum the result 
     for (int i=0; i<3; i++){ 
     for (int j=0; j<3; j++) { 
      convR += convolvedMatR[i][j]; 
      convG += convolvedMatG[i][j]; 
      convB += convolvedMatB[i][j]; 
     } 
     } 
     sum = vec4(vec3(convR, convG, convB), 1.0); 

    } 
    else if(gl_TexCoord[0].x >0.51) 
    { 
     sum = texture2D(colorMap, gl_TexCoord[0].xy); 
    } 
    else // Draw a red line 
    { 
     sum = vec4(1.0, 0.0, 0.0, 1.0); 
    } 

    gl_FragColor = sum; 
}