【 OpenGL】高斯模糊

  • Post author:
  • Post category:其他


[video(video-83qMkG9M-1628401078616)(type-bilibili)(url-https://player.bilibili.com/player.html?aid=762197327)(image-https://img-blog.csdnimg.cn/img_convert/1607e0b671b2d2ac8251654fa64b5633.png)(title-【opengl】高斯模糊)]

请添加图片描述

6+6次水平垂直高斯模糊效果:

请添加图片描述

12+12次水平垂直高斯模糊效果:

请添加图片描述

50+50次水平垂直高斯模糊效果:

请添加图片描述

  • gles中的算法实现:
#version 130

out vec4 FragColor;
in vec2 TexCoords;

uniform sampler2D image;

uniform bool horizontal;

uniform float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);

void main()
{     
	//模糊步长,最精细为像素级,补偿越小颗粒感越低,同时每次模糊的效果也越低        
    vec2 tex_offset = 2.0 / textureSize(image, 0); // gets size of single texel
    vec3 result = texture(image, TexCoords).rgb * weight[0]; // current fragment's contribution
    if(horizontal)
    {
        for(int i = 1; i < 5; ++i)
        {
            result += texture(image, TexCoords + vec2(tex_offset.x * i, 0.0)).rgb * weight[i];
            result += texture(image, TexCoords - vec2(tex_offset.x * i, 0.0)).rgb * weight[i];
        }
    }
    else
    {
        for(int i = 1; i < 5; ++i)
        {
            result += texture(image, TexCoords + vec2(0.0, tex_offset.y * i)).rgb * weight[i];
            result += texture(image, TexCoords - vec2(0.0, tex_offset.y * i)).rgb * weight[i];
        }
    }
    FragColor = vec4(result, 1.0);
}

片段着色器中只是对所有像素乘以高斯核,完成一次水平或者垂直的模糊,要想控制模糊程度需要CPU代码进行多次模糊。

对应的顶点着色器程序:

#version 130
in vec3 aPos; // 位置变量的属性位置值为0
in vec3 aColor;
in vec2 aTexCoord;

out vec3 ourColor;
out vec2 TexCoords;

void main()
{
	gl_Position = vec4(aPos, 1.0);
	ourColor = aColor;
	TexCoords = vec2(aTexCoord.x, aTexCoord.y);
}
  • OpenGL乒乓离线帧缓存:

    参考:

    LearnOpenGL CN专栏 作者:JoeyDeVries


    基本思想是:申请两块帧缓存,第一次对原图纹理做一次水平高斯模糊,将结果放入其中一块帧缓存对应的纹理内存。第二次对上一步完成渲染纹理再做垂直高斯模糊,结果放入另一块纹理内存。如此交替进行,类似乒乓球过程,整个过程不进行显示双缓冲的交换,不会显示到屏幕,因此也叫离屏渲染。

    生成乒乓帧缓存片段:
	glGenFramebuffers(2, pingpongFBO);
	glGenTextures(2, pingpongBuffers);
	for (unsigned int i = 0; i < 2; i++)
	{
		glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[i]);
		glBindTexture(GL_TEXTURE_2D, pingpongBuffers[i]);
		//大坑!!!这里的纹理尺寸需要与屏幕(视口)像素尺寸一致,当图片尺寸大于屏幕尺寸时,会导致纹理内存填不满
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingpongBuffers[i], 0);
		// also check if framebuffers are complete (no need for depth buffer)
		if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
		    std::cout << "Framebuffer not complete!" << std::endl;
	}

进行乒乓渲染片段:

void blur()
{
	GLboolean horizontal = true;
	GLboolean first_iteration = true;
	GLuint amount = 100;
	mshader.use();
	for (GLuint i = 0; i < amount; i++)
	{
		glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[horizontal]); 
		glUniform1i(glGetUniformLocation(mshader.ID, "horizontal"), horizontal);
		glBindTexture(
		GL_TEXTURE_2D, first_iteration ? texture1 : pingpongBuffers[!horizontal]); 


		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
		///*  测试单步效果
	    glBindFramebuffer(GL_FRAMEBUFFER, 0);
		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
                glutSwapBuffers();
		usleep(500 * 1000 );
                //*/
		horizontal = !horizontal;
		if (first_iteration)
		first_iteration = false;
	}
	//可以实现GPU渲染完成再返回,类似linux的barrier机制
	glFinish();
}

完整代码:

main.cpp

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#include <shader.h>
#include <GL/glut.h>
#include <stdlib.h>


#include <string>
#include <fstream>
#include <sstream>
#include <iostream>

#include <unistd.h>
#include <sys/time.h>
#include<stdio.h>


// settings
const unsigned int SCR_WIDTH = 960;
const unsigned int SCR_HEIGHT = 540;
static Shader mshader;
unsigned int texture1;
unsigned int VBO, VAO, EBO;
GLboolean horizontal = true;
GLuint pingpongFBO[2];
GLuint pingpongBuffers[2];

void reshape(int w, int h);
void display();
void resourceready();
void blur();
int main(int argc, char **argv)
{

 	#define MILLION 1000000
	struct timeval tv1,tv2;
        long sec,usec;

     
        glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
	glutInitWindowPosition(100, 100);
	glutInitWindowSize(SCR_WIDTH, SCR_HEIGHT);
	glutCreateWindow("SHADER_RECTANGLE");
	glewInit();


	const GLubyte* name = glGetString(GL_VENDOR); //返回负责当前OpenGL实现厂商的名字
	const GLubyte* biaoshifu = glGetString(GL_RENDERER); //返回一个渲染器标识符,通常是个硬件平台
	const GLubyte* OpenGLVersion =glGetString(GL_VERSION); //返回当前OpenGL实现的版本号
	const GLubyte* GLESVersion =glGetString(GL_SHADING_LANGUAGE_VERSION); //返回当前OpenGL实现的版本号
	printf("OpenGL实现厂商的名字:%s\n", name);
	printf("渲染器标识符:%s\n", biaoshifu);
	printf("OpenGL实现的版本号:%s\n",OpenGLVersion );
        printf("GLES语法的版本号:%s\n",GLESVersion );

        mshader= Shader("shader.vs", "shader.fs"); 
	resourceready();
	

        gettimeofday(&tv1, NULL);
        blur();
        gettimeofday(&tv2, NULL);
	sec = (tv2.tv_sec - tv1.tv_sec);
	usec = (tv2.tv_usec - tv1.tv_usec);
        
        std::cout << "---------tmie cost---------------" << sec <<"(s)  "<< usec<<"(us) " << std::endl;

	glutReshapeFunc(&reshape);
	glutDisplayFunc(&display);
	glutMainLoop();




    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    //glDeleteVertexArrays(1, &VAO);
    //glDeleteBuffers(1, &VBO);
    //glDeleteBuffers(1, &EBO);


    return 0;
}

void reshape(int w, int h)
{
	glViewport(0, 0,(GLsizei)w, (GLsizei)h);
}


void resourceready()
{
	// set up vertex data (and buffer(s)) and configure vertex attributes
	// ------------------------------------------------------------------
	float vertices[] = {
	// positions          // colors           // texture coords
	 1.0f,  1.0f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f, // top right
	 1.0f, -1.0f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f, // bottom right
	-1.0f, -1.0f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f, // bottom left
	-1.0f,  1.0f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f  // top left 
	};
	unsigned int indices[] = {  
	0, 1, 3, // first triangle
	1, 2, 3  // second triangle
	};

	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	glGenBuffers(1, &EBO);

	glBindVertexArray(VAO);

	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

	// position attribute
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);
	// color attribute
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);
	// texture coord attribute
	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
	glEnableVertexAttribArray(2);


	// load and create a texture 
	// -------------------------
	glGenTextures(1, &texture1);
	glBindTexture(GL_TEXTURE_2D, texture1); // all upcoming GL_TEXTURE_2D operations now have effect on this texture object
	// set the texture wrapping parameters
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);	// set texture wrapping to GL_REPEAT (default wrapping method)
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
	// set texture filtering parameters
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	// load image, create texture and generate mipmaps
	int width, height, nrChannels;

        stbi_set_flip_vertically_on_load(true);	
	unsigned char *data = stbi_load("1080.jpeg", &width, &height, &nrChannels, 0);
	if (data)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);
		std::cout << "texture1 width  " <<width << "  hight   " << height  <<"  nrChannels  " << nrChannels<< std::endl;
	}
	else
	{
	std::cout << "Failed to load texture" << std::endl;
	}
	stbi_image_free(data);


	glGenFramebuffers(2, pingpongFBO);
	glGenTextures(2, pingpongBuffers);
	for (unsigned int i = 0; i < 2; i++)
	{
		glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[i]);
		glBindTexture(GL_TEXTURE_2D, pingpongBuffers[i]);
		//大坑!!!这里的纹理尺寸需要与屏幕(视口)像素尺寸一致,当图片尺寸大于屏幕尺寸时,会导致纹理内存填不满
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingpongBuffers[i], 0);
		// also check if framebuffers are complete (no need for depth buffer)
		if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
		    std::cout << "Framebuffer not complete!" << std::endl;
	}

}

void blur()
{
	GLboolean first_iteration = true;
	GLuint amount = 100;
	mshader.use();
	for (GLuint i = 0; i < amount; i++)
	{
		glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[horizontal]); 
		glUniform1i(glGetUniformLocation(mshader.ID, "horizontal"), horizontal);
		glBindTexture(
		GL_TEXTURE_2D, first_iteration ? texture1 : pingpongBuffers[!horizontal]); 
		//std::cout << "---------------------------loop:" << i << std::endl;

		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
		///*  测试单步效果
	        glBindFramebuffer(GL_FRAMEBUFFER, 0);
		glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
                glutSwapBuffers();
		usleep(500 * 1000 );
                //*/
		horizontal = !horizontal;
		if (first_iteration)
		first_iteration = false;
	}
	glFinish();
}

void display()
{

	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	// render
	// ------
	glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT);

	// bind Texture
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, pingpongBuffers[!horizontal]);

	// render container
	mshader.use();
        glBindVertexArray(VAO);
	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

	//glDisableVertexAttribArray(0);
	
        glutSwapBuffers();
}

依赖库:


stb_image.h下载



stb_image.h使用

实验步骤:

  1. 编译

    g++ main.cpp -o test -lGL -lglut -lGLEW -I.
  2. 对应目录放图片,命名1080.jpeg
  3. ./test
  4. 耗时情况:
OpenGL实现厂商的名字:VMware, Inc.
渲染器标识符:SVGA3D; build: RELEASE;  LLVM;
OpenGL实现的版本号:3.0 Mesa 18.0.5
GLES语法的版本号:1.30
texture1 width  1920  hight   1080  nrChannels  3
---------time cost---------------0(s)  72273(us) 



版权声明:本文为ludashei2原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。