孤独明镜

菩提本无树,明镜亦非台,本来无一物,何处惹尘埃。

嗨,我是Dragon,一名全栈开发者。现居上海,就职于一家游戏开发公司。做过Linux/QT、WEB(PHP/Python)、游戏开发,目前主攻图形/图像方向,对数学、文学、哲学、摄影等非常的喜爱。


使用OpenGL Shader实现放大镜效果

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

uniform float texture_id;//当前使用的纹理的ID

void main()
{
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;//输出默认顶点位置
    gl_FrontColor = gl_Color;
    gl_TexCoord[texture_id] = gl_TextureMatrix[texture_id] * gl_MultiTexCoord0;//输出当前顶点对应的纹理坐标。
}

再来看一下片段着色器。

uniform vec2  in_circle_pos;//从客户端传入的放大镜圆心位置
uniform float in_circle_radius;//从客户端传入的放大镜圆半径
uniform float in_zoom_times;//从客户端传入的放大镜放大倍数

uniform float imageWidth;//从客户端传入的图片宽数据
uniform float imageHeight;//从客户端传入的图片高数据

uniform float texture_id;//从客户端传入的纹理ID数据

uniform sampler2D textureSampler;//从客户端传入的采样器ID

vec2 transForTexPosition(vec2 pos)
{
    return vec2((float)pos.x/imageWidth, (float)pos.y/imageHeight);
}

float getDistance(vec2 pos_src, vec2 pos_dist)
{
    float quadratic_sum = pow((pos_src.x - pos_dist.x), 2) + pow((pos_src.y - pos_dist.y), 2);
    return sqrt(quadratic_sum);
}

vec2 getZoomPosition()
{
    float zoom_x = (float)(gl_FragCoord.x - in_circle_pos.x) / in_zoom_times;
    float zoom_y = (float)(gl_FragCoord.y - in_circle_pos.y) / in_zoom_times;
    return vec2((float)in_circle_pos.x + zoom_x, (float)in_circle_pos.y - zoom_y);
}

vec4 getColor()//双线性插值采样
{
    vec2 pos = getZoomPosition();

    float _x = floor(pos.x);
    float _y = floor(pos.y);

    float u = pos.x - _x;
    float v = pos.y - _y;

    vec4 data_00 = texture2D(textureSampler, transForTexPosition(vec2(_x, _y)));
    vec4 data_01 = texture2D(textureSampler, transForTexPosition(vec2(_x, _y + 1)));
    vec4 data_10 = texture2D(textureSampler, transForTexPosition(vec2(_x + 1, _y)));
    vec4 data_11 = texture2D(textureSampler, transForTexPosition(vec2(_x + 1, _y + 1)));

    return (1 - u) * (1 - v) * data_00 + (1 - u) * v * data_01 + u * (1 - v) * data_10 + u * v * data_11;
}

void main()
{
    vec2 frag_pos = vec2(gl_FragCoord.x, gl_FragCoord.y);
    //若当前片段位置距放大镜圆心距离大于圆半径时,直接从纹理中采样输出片段颜色
    if (getDistance(in_circle_pos, frag_pos) > in_circle_radius)
        gl_FragColor = texture2D(textureSampler, gl_TexCoord[texture_id].st);
    else //距离小于半径的片段,二次线性插值获得顔色。
        gl_FragColor = getColor();
}

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

m_circle_pos = glGetUniformLocation(m_program, "in_circle_pos");
m_circle_radius = glGetUniformLocation(m_program, "in_circle_radius");
m_zoom_times = glGetUniformLocation(m_program, "in_zoom_times");

m_image_width = glGetUniformLocation(m_program, "imageWidth");
m_image_height = glGetUniformLocation(m_program, "imageHeight");
m_texture_sampler = glGetUniformLocation(m_program, "textureSampler");
m_texture_id  = glGetUniformLocation(m_program, "texture_id");

对上面获取的变量赋值。

glUniform2f(m_circle_pos, circle_pos.x(), circle_pos.y());
glUniform1f(m_circle_radius, (float)100.0);
glUniform1f(m_zoom_times, (float)2.0);

glUniform1f(m_image_width, (float)this->width);
glUniform1f(m_image_height, (float)this->height);

glUniform1i(m_texture_sampler, kTexture);
glUniform1i(m_texture_id, kTexture);

画图。

glBindTexture(GL_TEXTURE_2D, m_textures[kTexture]);

glBegin(GL_QUADS);
    glTexCoord2f(0.0, 0.0);glVertex3f(-this->width/2, this->height/2, 0.0);
    glTexCoord2f(0.0, 1.0);glVertex3f(-this->width/2, -this->height/2, 0.0);
    glTexCoord2f(1.0, 1.0);glVertex3f(this->width/2, -this->height/2, 0.0);
    glTexCoord2f(1.0, 0.0);glVertex3f(this->width/2, this->height/2, 0.0);
glEnd();

最终效果如下:

转载请注明地址:孤独明镜