GLSL 径向模糊的奇怪问题

Posted

技术标签:

【中文标题】GLSL 径向模糊的奇怪问题【英文标题】:Weird issue with GLSL Radial Blur 【发布时间】:2013-05-02 04:32:03 【问题描述】:

使用的着色器:http://www.gamerendering.com/2008/12/20/radial-blur-filter/

我的问题是这样的:整个场景只占用了四分之一的屏幕空间(就是这些坐标的一个矩形:width/2,0 width,height/2),而且好像被字面剪裁了(未缩放)。它还用 Y 倒置来渲染它(但我可以通过从第 9 行的 gl_Position.y 中删除 - 符号来解决这个问题)。

我使用this tutorial 进行后处理,除了使用基本的glBegin(GL_QUADS) 而不是顶点数组。


编辑 2:我尝试过使用另一个着色器 (http://www.geeks3d.com/20110428/shader-library-swirl-post-processing-filter-in-glsl/),效果非常好。没有剪辑。

编辑:这是着色器的来源(我做了一些小的编辑):

片段着色器

#version 140

// This texture should hold the image to blur.
// This can perhaps be a previous rendered scene.
uniform sampler2D tex;

// texture coordinates passed from the vertex shader
varying vec2 uv;

// some const, tweak for best look
const float sampleDist = 1.0;
const float sampleStrength = 20.2; 

void main(void)

   // some sample positions
   float samples[10] =
   float[](-0.08,-0.05,-0.03,-0.02,-0.01,0.01,0.02,0.03,0.05,0.08);

    // 0.5,0.5 is the center of the screen
    // so substracting uv from it will result in
    // a vector pointing to the middle of the screen
    vec2 dir = 0.5 - uv; 

    // calculate the distance to the center of the screen
    float dist = sqrt(dir.x*dir.x + dir.y*dir.y); 

    // normalize the direction (reuse the distance)
    //dir = dir/dist; 
    dir /= dist;

    // this is the original colour of this fragment
    // using only this would result in a nonblurred version
    vec4 color = texture2D(tex,uv); 

    vec4 sum = color;

    // take 10 additional blur samples in the direction towards
    // the center of the screen
    for (int i = 0; i < 10; i++)
    
      sum += texture2D( tex, uv + dir * samples[i] * sampleDist );
    

    // we have taken eleven samples
    sum *= 1.0/11.0;

    // weighten the blur effect with the distance to the
    // center of the screen ( further out is blurred more)
    float t = dist * sampleStrength;
    t = clamp( t ,0.0,1.0); //0 <= t <= 1

    //Blend the original color with the averaged pixels
    gl_FragColor = mix( color, sum, t );

顶点着色器

#version 140

varying vec2  uv;
// this vertex shader is from AMD RenderMonkey
void main(void)

   gl_Position = vec4( gl_Vertex.xy, 0.0, 1.0 );
   gl_Position = sign( gl_Position );

   // Texture coordinate for screen aligned (in correct range):
   uv = (vec2( gl_Position.x, gl_Position.y ) + vec2(1.0 )) / vec2( 2.0 );

后处理器

#include "std/opengl.h"

#include "postprocess.h"

Post::Post(Pos2D res) 
        this->res = res;
        glActiveTexture(GL_TEXTURE0);
        glGenTextures(1, &this->texture);
        glBindTexture(GL_TEXTURE_2D, this->texture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, res.x, res.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
        glBindTexture(GL_TEXTURE_2D, 0);
        glGenRenderbuffers(1, &this->rbo);
        glBindRenderbuffer(GL_RENDERBUFFER, this->rbo);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, res.x, res.y);
        glBindRenderbuffer(GL_RENDERBUFFER, 0);
        glGenFramebuffers(1, &this->fbo);
        glBindFramebuffer(GL_FRAMEBUFFER, this->fbo);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->texture, 0);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, this->rbo);
        GLenum status;
        if ((status = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) 
                fprintf(stderr, "Framebuffer error! %i", status);
        
        glBindFramebuffer(GL_FRAMEBUFFER, 0);


Post::~Post() 
        glDeleteRenderbuffers(1, &this->rbo);
        glDeleteTextures(1, &this->texture);
        glDeleteFramebuffers(1, &this->fbo);


void Post::update(Pos2D res) 
        this->res = res;
        glBindTexture(GL_TEXTURE_2D, this->texture);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, res.x, res.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
        glBindTexture(GL_TEXTURE_2D, 0);
        glBindRenderbuffer(GL_RENDERBUFFER, this->rbo);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, res.x, res.y);
        glBindRenderbuffer(GL_RENDERBUFFER, 0);


void Post::bind() 
        glBindFramebuffer(GL_FRAMEBUFFER, this->fbo);


void Post::unbind() 
        glBindFramebuffer(GL_FRAMEBUFFER, 0);


void Post::render(Shader* shader) 
        glClear(GL_COLOR_BUFFER_BIT);
        glClearColor(0.0, 0.0, 0.0, 1.0);

        shader->run();
        auto tex = glGetUniformLocation(shader->program_handle, "tex");
        glBindTexture(GL_TEXTURE_2D, this->texture);
        glUniform1i(tex, 0);
        glBegin(GL_QUADS);
          glTexCoord2f(0.0, 0.0);
          glVertex2f(0, 0);
          glTexCoord2f(0.0, 1.0);
          glVertex2f(0, this->res.y);
          glTexCoord2f(1.0, 1.0);
          glVertex2f(this->res.x, this->res.y);
          glTexCoord2f(1.0, 0.0);
          glVertex2f(this->res.x, 0);
        glEnd();
        glBindTexture(GL_TEXTURE_2D, 0);
        glUseProgram(0);

问题的屏幕截图(如您所见,已被截断)

我禁用着色器时的屏幕截图(如您所见,它可以渲染到整个窗口,它没有被裁剪)

【问题讨论】:

【参考方案1】:

问题来自于您定义顶点坐标的方式:

    glTexCoord2f(0.0, 0.0);
    glVertex2f(0, 0);
    glTexCoord2f(0.0, 1.0);
    glVertex2f(0, this->res.y);
    glTexCoord2f(1.0, 1.0);
    glVertex2f(this->res.x, this->res.y);
    glTexCoord2f(1.0, 0.0);
    glVertex2f(this->res.x, 0);

不要使用窗口分辨率作为顶点坐标,而是尝试使用以下顶点绘制一个四边形:( -1, -1 )、( -1, 1 )、( 1, 1 ) 和 (1, -1 ) -这将导致在 opengl 的本机坐标系中出现全屏四边形。在您的顶点着色器中,在我看来您正在尝试使用符号函数将顶点坐标转换为该坐标系:

   gl_Position = vec4( gl_Vertex.xy, 0.0, 1.0 );
   gl_Position = sign( gl_Position );

但是当然你会得到一个带有角点 (0, 0)、(0, 1)、(1, 1) 和 (1, 0) 的四边形,这就解释了为什么它只填充了部分屏幕。 因此,您在着色器中使用此行计算的纹理坐标:

uv = (vec2( gl_Position.x, gl_Position.y ) + vec2(1.0 )) / vec2( 2.0 );

也是错误的,导致您遇到“剪辑”外观 - 您的 texcoords 范围从 0.5 到 1,而不是 0 到 1。顺便说一句,无论如何,您都使用 glTexCoord2f 明确定义 texcoords,那些 是正确的 - 所以不需要在着色器中再次计算它们,只需使用内置的 gl_MultiTexcoord。

【讨论】:

谢谢,改变顶点工作!但是我是着色器的新手(我几乎从帖子中复制+粘贴了这个),你能解释一下我应该用gl_Texcoord替换哪些部分吗? 尝试用 uv = gl_MultiTexCoord0.xy 替换顶点着色器中的最后一行; 基本上,当您使用 glVertex2f、glTexcoord2f 等固定功能命令时,可以通过一些预定义的名称在着色器中访问这些值。您使用 gl_Vertex 访问顶点位置,使用 gl_MultiTexcoord0 访问纹理坐标(gl_Color 和 gl_Normal 也存在) 哦,好吧...我想知道...非常感谢您的解释:D

以上是关于GLSL 径向模糊的奇怪问题的主要内容,如果未能解决你的问题,请参考以下文章

使用 CSS 径向渐变添加模糊

GLSL对非纹理矩形的模糊效果

径向模糊(Radial Blur)

使用带有径向渐变的 CAGradientLayer 时出现奇怪的渐变。

径向模糊

在UnrealEngine中用Custom节点实现径向模糊