使用OpenGL Shader实现放大镜效果

http://outofmemory.cn/wr/?u=http%3A%2F%2Fwww.renjihe.com%2Findex.php%2Farchives%2F776


周末闲来无事,想玩玩OpenGL Shader,想想就实现一个放大镜效果的Shader吧。
着色器可以指定放大镜位置、半径、及放大部数,我实现是在片段着色中使用向后映射的双线性插值的方式对当前片段颜色进行插值,而顶点着色器什么都不做。
先来看一下顶点着色器。

1 uniform float texture_id;//当前使用的纹理的ID
2  
3 void main()
4 {
5     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;//输出默认顶点位置
6     gl_FrontColor = gl_Color;
7     gl_TexCoord[texture_id] = gl_TextureMatrix[texture_id] * gl_MultiTexCoord0;//输出当前顶点对应的纹理坐标。
8 }

再来看一下片段着色器。

01 uniform vec2  in_circle_pos;//从客户端传入的放大镜圆心位置
02 uniform float in_circle_radius;//从客户端传入的放大镜圆半径
03 uniform float in_zoom_times;//从客户端传入的放大镜放大倍数
04  
05 uniform float imageWidth;//从客户端传入的图片宽数据
06 uniform float imageHeight;//从客户端传入的图片高数据
07  
08 uniform float texture_id;//从客户端传入的纹理ID数据
09  
10 uniform sampler2D textureSampler;//从客户端传入的采样器ID
11  
12 vec2 transForTexPosition(vec2 pos)
13 {
14     return vec2((float)pos.x/imageWidth, (float)pos.y/imageHeight);
15 }
16  
17 float getDistance(vec2 pos_src, vec2 pos_dist)
18 {
19     float quadratic_sum = pow((pos_src.x - pos_dist.x), 2) + pow((pos_src.y - pos_dist.y), 2);
20     return sqrt(quadratic_sum);
21 }
22  
23 vec2 getZoomPosition()
24 {
25     float zoom_x = (float)(gl_FragCoord.x - in_circle_pos.x) / in_zoom_times;
26     float zoom_y = (float)(gl_FragCoord.y - in_circle_pos.y) / in_zoom_times;
27     return vec2((float)in_circle_pos.x + zoom_x, (float)in_circle_pos.y - zoom_y);
28 }
29  
30 vec4 getColor()//双线性插值采样
31 {
32     vec2 pos = getZoomPosition();
33  
34     float _x = floor(pos.x);
35     float _y = floor(pos.y);
36  
37     float u = pos.x - _x;
38     float v = pos.y - _y;
39  
40     vec4 data_00 = texture2D(textureSampler, transForTexPosition(vec2(_x, _y)));
41     vec4 data_01 = texture2D(textureSampler, transForTexPosition(vec2(_x, _y + 1)));
42     vec4 data_10 = texture2D(textureSampler, transForTexPosition(vec2(_x + 1, _y)));
43     vec4 data_11 = texture2D(textureSampler, transForTexPosition(vec2(_x + 1, _y + 1)));
44  
45     return (1 - u) * (1 - v) * data_00 + (1 - u) * v * data_01 + u * (1 - v) * data_10 + u * v * data_11;
46 }
47  
48 void main()
49 {
50     vec2 frag_pos = vec2(gl_FragCoord.x, gl_FragCoord.y);
51     //若当前片段位置距放大镜圆心距离大于圆半径时,直接从纹理中采样输出片段颜色
52     if (getDistance(in_circle_pos, frag_pos) > in_circle_radius)
53         gl_FragColor = texture2D(textureSampler, gl_TexCoord[texture_id].st);
54     else//距离小于半径的片段,二次线性插值获得顔色。
55         gl_FragColor = getColor();
56 }

再来看看一看客户端主要操作。
从编译的Shader程序中获取变量,用于后续操作。

1 m_circle_pos = glGetUniformLocation(m_program, "in_circle_pos");
2 m_circle_radius = glGetUniformLocation(m_program, "in_circle_radius");
3 m_zoom_times = glGetUniformLocation(m_program, "in_zoom_times");
4  
5 m_image_width = glGetUniformLocation(m_program, "imageWidth");
6 m_image_height = glGetUniformLocation(m_program, "imageHeight");
7 m_texture_sampler = glGetUniformLocation(m_program, "textureSampler");
8 m_texture_id  = glGetUniformLocation(m_program, "texture_id");

对上面获取的变量赋值。

1 glUniform2f(m_circle_pos, circle_pos.x(), circle_pos.y());
2 glUniform1f(m_circle_radius, (float)100.0);
3 glUniform1f(m_zoom_times, (float)2.0);
4  
5 glUniform1f(m_image_width, (float)this->width);
6 glUniform1f(m_image_height, (float)this->height);
7  
8 glUniform1i(m_texture_sampler, kTexture);
9 glUniform1i(m_texture_id, kTexture);

画图。

1 glBindTexture(GL_TEXTURE_2D, m_textures[kTexture]);
2  
3 glBegin(GL_QUADS);
4     glTexCoord2f(0.0, 0.0);glVertex3f(-this->width/2, this->height/2, 0.0);
5     glTexCoord2f(0.0, 1.0);glVertex3f(-this->width/2, -this->height/2, 0.0);
6     glTexCoord2f(1.0, 1.0);glVertex3f(this->width/2, -this->height/2, 0.0);
7     glTexCoord2f(1.0, 0.0);glVertex3f(this->width/2, this->height/2, 0.0);
8 glEnd();

最终效果如下:

作者:renjihe 出处: http://www.renjihe.com
本站所有文章均是原创,版权归本站所有。欢迎转载,但未经同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

你可能感兴趣的:(OpenGL)