dud*_*saw 10 opengl shader glsl
我正在使用 libgdx,想要制作一个常见的高斯模糊后处理效果。按照本指南,我遇到了纹理过滤的一些问题。这里是图像:
实际图像:

半径 = 1 时模糊:

半径 = 5 进行模糊处理:

在最简单的情况下,我只有一个透明的帧缓冲区对象,其中渲染了一些对象。然后我需要对此应用一些着色器效果,基本上只是模糊,然后将结果渲染到屏幕上。我还想调整模糊半径,但如果我将半径设置为大于 1,它就会看起来非常粗糙。我猜它应该与一些线性过滤一起使用,但它不在这里。所以我只需要应用相同的效果与软模糊和可配置的半径,也许还有一些其他着色器侧选项。我还尝试将线性过滤显式分配给 FBO 纹理,这不会改变任何内容。
片段着色器:
//"in" attributes from our vertex shader
varying vec4 vColor;
varying vec2 vTexCoord;
//declare uniforms
uniform sampler2D u_texture;
uniform float resolution;
uniform float radius;
uniform vec2 dir;
void main() {
//this will be our RGBA sum
vec4 sum = vec4(0.0);
//our original texcoord for this fragment
vec2 tc = vTexCoord;
//the amount to blur, i.e. how far off center to sample from
//1.0 -> blur by one pixel
//2.0 -> blur by two pixels, etc.
float blur = radius/resolution;
//the direction of our blur
//(1.0, 0.0) -> x-axis blur
//(0.0, 1.0) -> y-axis blur
float hstep = dir.x;
float vstep = dir.y;
//apply blurring, using a 9-tap filter with predefined gaussian weights
sum += texture2D(u_texture, vec2(tc.x - 4.0*blur*hstep, tc.y - 4.0*blur*vstep)) * 0.0162162162;
sum += texture2D(u_texture, vec2(tc.x - 3.0*blur*hstep, tc.y - 3.0*blur*vstep)) * 0.0540540541;
sum += texture2D(u_texture, vec2(tc.x - 2.0*blur*hstep, tc.y - 2.0*blur*vstep)) * 0.1216216216;
sum += texture2D(u_texture, vec2(tc.x - 1.0*blur*hstep, tc.y - 1.0*blur*vstep)) * 0.1945945946;
sum += texture2D(u_texture, vec2(tc.x, tc.y)) * 0.2270270270;
sum += texture2D(u_texture, vec2(tc.x + 1.0*blur*hstep, tc.y + 1.0*blur*vstep)) * 0.1945945946;
sum += texture2D(u_texture, vec2(tc.x + 2.0*blur*hstep, tc.y + 2.0*blur*vstep)) * 0.1216216216;
sum += texture2D(u_texture, vec2(tc.x + 3.0*blur*hstep, tc.y + 3.0*blur*vstep)) * 0.0540540541;
sum += texture2D(u_texture, vec2(tc.x + 4.0*blur*hstep, tc.y + 4.0*blur*vstep)) * 0.0162162162;
gl_FragColor = vColor * sum;
}
Run Code Online (Sandbox Code Playgroud)
全班
据我所知,你根本没有进行高斯模糊......
图像上的高斯模糊是图像和分辨率的高斯加权矩阵之间的卷积1+2*r,其中r是模糊的半径。r因此,输出的颜色应该是距目标像素距离为 的所有像素的加权和。
你所做的只是 9 个像素的加权和,无论半径是错误的(在我的选择中),因为你应该对~6.28*r*r像素求和。所以我希望有 2 个嵌套的 for 循环......
这是我刚刚收集到的一个 GLSL 小例子:
//---------------------------------------------------------------------------
// Fragment
//---------------------------------------------------------------------------
#version 420 core
//---------------------------------------------------------------------------
in vec2 pos; // screen position <-1,+1>
out vec4 gl_FragColor; // fragment output color
uniform sampler2D txr; // texture to blur
uniform float xs,ys; // texture resolution
uniform float r; // blur radius
//---------------------------------------------------------------------------
void main()
{
float x,y,xx,yy,rr=r*r,dx,dy,w,w0;
w0=0.3780/pow(r,1.975);
vec2 p;
vec4 col=vec4(0.0,0.0,0.0,0.0);
for (dx=1.0/xs,x=-r,p.x=0.5+(pos.x*0.5)+(x*dx);x<=r;x++,p.x+=dx){ xx=x*x;
for (dy=1.0/ys,y=-r,p.y=0.5+(pos.y*0.5)+(y*dy);y<=r;y++,p.y+=dy){ yy=y*y;
if (xx+yy<=rr)
{
w=w0*exp((-xx-yy)/(2.0*rr));
col+=texture2D(txr,p)*w;
}}}
gl_FragColor=col;
}
//---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
输出r=5:
我对权重进行了归一化,因此无论选择什么,亮度都不会从原始纹理变化太大r...但是,r=5误差是最大的,所有其他半径似乎要好得多...这可能是因为圆周与圆的混叠内部状况...
您还应该添加一些边缘情况处理(当在纹理边缘附近进行卷积时),因为求和像素的数量不同,因此应相应地缩放系数。
描述上面的代码:这两个循环只是循环遍历r从posin到半径的所有像素texture,并将像素x,y和 NDC转换pos为纹理坐标p。然后,对于每个位置,计算高斯权重,然后将其用于加权和。所有这些之后,输出结果颜色。
[编辑1] 2遍方法
我将其移植到 2 通道渲染(首先集成水平线,然后垂直),我得到了这个输出 ( r=5):
这里顶点着色器:
//---------------------------------------------------------------------------
// Vertex
//---------------------------------------------------------------------------
#version 420 core
//---------------------------------------------------------------------------
layout(location=0) in vec4 vertex;
out vec2 pos; // screen position <-1,+1>
void main()
{
pos=vertex.xy;
gl_Position=vertex;
}
//---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
这里是片段着色器:
//---------------------------------------------------------------------------
// Fragment
//---------------------------------------------------------------------------
#version 420 core
//---------------------------------------------------------------------------
in vec2 pos; // screen position <-1,+1>
out vec4 gl_FragColor; // fragment output color
uniform sampler2D txr; // texture to blur
uniform float xs,ys; // texture resolution
uniform float r; // blur radius
uniform int axis;
//---------------------------------------------------------------------------
void main()
{
float x,y,rr=r*r,d,w,w0;
vec2 p=0.5*(vec2(1.0,1.0)+pos);
vec4 col=vec4(0.0,0.0,0.0,0.0);
w0=0.5135/pow(r,0.96);
if (axis==0) for (d=1.0/xs,x=-r,p.x+=x*d;x<=r;x++,p.x+=d){ w=w0*exp((-x*x)/(2.0*rr)); col+=texture2D(txr,p)*w; }
if (axis==1) for (d=1.0/ys,y=-r,p.y+=y*d;y<=r;y++,p.y+=d){ w=w0*exp((-y*y)/(2.0*rr)); col+=texture2D(txr,p)*w; }
gl_FragColor=col;
}
//---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
并且为了确保 CPU 端 C++/VCL/OpenGL 代码(旧的 api 保持简单):
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
GLuint txr_img,txr_scr;
//---------------------------------------------------------------------------
void gl_draw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(prog_id);
glUniform1i(glGetUniformLocation(prog_id,"txr" ),0);
glUniform1f(glGetUniformLocation(prog_id,"xs" ),xs);
glUniform1f(glGetUniformLocation(prog_id,"ys" ),ys);
glUniform1f(glGetUniformLocation(prog_id,"r" ),15.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txr_img);
glUniform1i(glGetUniformLocation(prog_id,"axis"),0);
glBegin(GL_QUADS);
glColor3f(1,1,1);
glVertex2f(-1.0,-1.0);
glVertex2f(-1.0,+1.0);
glVertex2f(+1.0,+1.0);
glVertex2f(+1.0,-1.0);
glEnd();
glBindTexture(GL_TEXTURE_2D,txr_scr);
glUniform1i(glGetUniformLocation(prog_id,"axis"),1);
BYTE *dat=new BYTE[xs*ys*4];
if (dat!=NULL)
{
glReadPixels(0,0,xs,ys,GL_BGRA,GL_UNSIGNED_BYTE,dat);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xs, ys, 0, GL_BGRA, GL_UNSIGNED_BYTE, dat);
delete[] dat;
}
glBegin(GL_QUADS);
glColor3f(1,1,1);
glVertex2f(-1.0,-1.0);
glVertex2f(-1.0,+1.0);
glVertex2f(+1.0,+1.0);
glVertex2f(+1.0,-1.0);
glEnd();
glUseProgram(0);
glBindTexture(GL_TEXTURE_2D,0);
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
gl_init(Handle);
// textures
Byte q;
unsigned int *pp;
int xs,ys,x,y,adr,*txr;
union { unsigned int c32; Byte db[4]; } c;
Graphics::TBitmap *bmp=new Graphics::TBitmap; // new bmp
// image texture
bmp->LoadFromFile("texture.bmp"); // load from file
bmp->HandleType=bmDIB; // allow direct access to pixels
bmp->PixelFormat=pf32bit; // set pixel to 32bit so int is the same size as pixel
xs=bmp->Width; // resolution should be power of 2
ys=bmp->Height;
txr=new int[xs*ys]; // create linear framebuffer
for(adr=0,y=0;y<ys;y++)
{
pp=(unsigned int*)bmp->ScanLine[y];
for(x=0;x<xs;x++,adr++)
{
// rgb2bgr and copy bmp -> txr[]
c.c32=pp[x];
q =c.db[2];
c.db[2]=c.db[0];
c.db[0]=q;
txr[adr]=c.c32;
}
}
glGenTextures(1,&txr_img);
glEnable(GL_TEXTURE_2D); // copy it to gfx card
glBindTexture(GL_TEXTURE_2D,txr_img);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xs, ys, 0, GL_RGBA, GL_UNSIGNED_BYTE, txr);
glDisable(GL_TEXTURE_2D);
delete[] txr;
delete bmp;
// screen texture
glGenTextures(1,&txr_scr);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,txr_scr);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE);
glDisable(GL_TEXTURE_2D);
int hnd,siz; char vertex[4096],fragment[4096];
hnd=FileOpen("blur.glsl_vert",fmOpenRead); siz=FileSeek(hnd,0,2); FileSeek(hnd,0,0); FileRead(hnd,vertex ,siz); vertex [siz]=0; FileClose(hnd);
hnd=FileOpen("blur.glsl_frag",fmOpenRead); siz=FileSeek(hnd,0,2); FileSeek(hnd,0,0); FileRead(hnd,fragment,siz); fragment[siz]=0; FileClose(hnd);
glsl_init(vertex,fragment);
hnd=FileCreate("GLSL.txt"); FileWrite(hnd,glsl_log,glsl_logs); FileClose(hnd);
ClientWidth=xs;
ClientHeight=ys;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
glDeleteTextures(1,&txr_img);
glDeleteTextures(1,&txr_scr);
gl_exit();
glsl_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
gl_resize(ClientWidth,ClientHeight);
gl_draw();
}
//---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)
可以gl_simple.h在这里找到:
这不是由于缺乏线性插值造成的。该着色器沿每个轴仅执行 9 次纹理提取。您不能指望仅采样 9 次并使用较大的内核获得平滑的模糊,因为您跳过了许多可能包含重要信息的像素。仅radius = 1有效。
对于更大的模糊,您要么需要更大的内核,要么多次应用较小的内核。
如果这变得太慢,要进行优化,您可以利用本文中的线性插值技术。由于线性插值允许您以 1 的价格计算两个相邻纹理像素之间的任意加权平均值,因此您可以获得仅执行 5 次纹理提取而不是 9 次的等效滤波器,或者使用 9 次纹理提取来获得大小为 17 的内核。从下采样图像金字塔中采样也是一种可能性。
顺便说一句,而不是这个冗长的东西:
vec2(tc.x - 4.0*blur*hstep, tc.y - 4.0*blur*vstep)
Run Code Online (Sandbox Code Playgroud)
你可以简单地写:
tc - 4.0*blur*dir
Run Code Online (Sandbox Code Playgroud)
其他 7 行也类似。