简体   繁体   English

OpenGL es 2.0 三角形上的高斯模糊

[英]OpenGL es 2.0 Gaussian blur on triangle

I recently learn opengl es 2.0, and now I try to make a gaussian blur on triangles generate by myself.我最近学习了opengl es 2.0,现在我尝试对自己生成的三角形进行高斯模糊。 I have some difficult to understand examples on the web and most apply the blur on an image.我在网络上有一些难以理解的示例,并且大多数将模糊应用于图像。 I know I have to use framebuffer but I don't know how to draw triangle on this and apply blur.我知道我必须使用帧缓冲区,但我不知道如何在其上绘制三角形并应用模糊。 Is it possible to see a real and complete code in C++ with good explication ?是否有可能在 C++ 中看到一个真实完整的代码并有很好的解释?

EDIT :编辑 :

#include <stdio.h>
#include <stdlib.h>
#include <iostream>

#define GLFW_INCLUDE_ES2
#include <GLFW/glfw3.h>
#include "shaders.hpp"
#include "camera.hpp"

unsigned int vbo, cbo, tbo;
GLuint _fbo, _fbo2, _tex, _tex2;


static const GLuint WIDTH = 800;
static const GLuint HEIGHT = 600;
GLuint pos, col, tex, normal;
camera * _camera = new camera();

static const GLfloat vertices[] = {
  0.0f,  1.0f, 0.0f,
  1.0f, -1.0f, 0.0f,
  -1.0f, -1.0f, 0.0f
};

static const GLfloat colors[] = {
  0.0f,  0.5f, 1.0f,
  0.5f,  0.5f, 1.0f,
  0.5f,  0.5f, 1.0f
};

static const GLfloat texture[] = {
  1.0f, 1.0f,
  1.0f, 0.0f,
  0.0f, 1.0f
};

int main(void){
  GLFWwindow* window;
  shaders * shaderBasic;
  GLuint pId;

  glm::mat4 projection; static glm::mat4 view; static glm::mat4 model;

  glfwInit();
  glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
  window = glfwCreateWindow(WIDTH, HEIGHT, __FILE__, NULL, NULL);
  glfwMakeContextCurrent(window);

  printf("GL_VERSION  : %s\n", glGetString(GL_VERSION) );
  printf("GL_RENDERER : %s\n", glGetString(GL_RENDERER) );

  std::string vs, fs;
  vs = "basic.vs";
  fs = "basic.fs";
  shaderBasic = new shaders(vs, fs);
  shaderBasic->CompileShader();
  shaderBasic->LinkShader();
  pId = shaderBasic->getProgramId();

  pos = glGetAttribLocation(pId, "position");
  col = glGetAttribLocation(pId, "colors");
  tex = glGetAttribLocation(pId, "tex");

  fs = "lastBlur.fs";
  shaders * blurShader;
  GLuint pIdBlur;
  blurShader = new shaders(vs, fs);
  blurShader->CompileShader();
  blurShader->LinkShader();
  pIdBlur = blurShader->getProgramId();

  _camera->setPositionCamera(glm::vec3(0, 0, -1));
  _camera->setLookAtCamera(glm::vec3(0, 0, 0));
  _camera->setFieldOfView(45);
  _camera->setAspect(WIDTH, HEIGHT);
  _camera->setViewport(WIDTH, HEIGHT);
  _camera->getMatricies(projection, view, model);

  glGenFramebuffers(1, &_fbo);
  glGenTextures(1, &_tex);
  glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
  glBindTexture(GL_TEXTURE_2D, _tex);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, WIDTH/2, HEIGHT/2, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
  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);
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _tex, 0);
  glBindTexture(GL_TEXTURE_2D, 0);
  glBindFramebuffer(GL_FRAMEBUFFER, 0);

  if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
  else{
    std::cout << "FRAMEBUFFER COMPLETE" << std::endl;
  }
  auto sampTex = glGetUniformLocation(pIdBlur, "texture0");
  std::cerr << "sampTex : " << sampTex << std::endl;
  glUniform1i(sampTex, 0);  
  while (!glfwWindowShouldClose(window)) {
    //    glViewport(0, 0, WIDTH, HEIGHT);

    glBindFramebuffer(GL_FRAMEBUFFER, _fbo);
    glClearColor(0.0f, 0.0f, 0.4f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    //    glViewport(0, 0, WIDTH/2, HEIGHT/2);
    glUseProgram(pIdBlur);
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(pos, 3, GL_FLOAT, false, 0, 0);
    glEnableVertexAttribArray(pos);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glGenBuffers(1, &cbo);
    glBindBuffer(GL_ARRAY_BUFFER, cbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
    glVertexAttribPointer(col, 2, GL_FLOAT, false, 0, 0);
    glEnableVertexAttribArray(col);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glGenBuffers(1, &tbo);
    glBindBuffer(GL_ARRAY_BUFFER, tbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(texture), texture, GL_STATIC_DRAW);
    glVertexAttribPointer(tex, 2, GL_FLOAT, false, 0, 0);
    glEnableVertexAttribArray(tex);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glDrawArrays(GL_TRIANGLES, 0, 3);


    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glUseProgram(pId);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, _tex);
    glDrawArrays(GL_TRIANGLES, 0, 3);    

    glfwPollEvents();
    glfwSwapBuffers(window);
  }
  glDeleteBuffers(1, &vbo);
  glfwTerminate();
  return EXIT_SUCCESS;
}

Blur Shader:模糊着色器:

#version 100
precision mediump float;

uniform sampler2D texture0;
varying vec3 vColor;
varying vec2 TexCoords;

vec4 blur13(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) {
    vec4 color = vec4(0.0);
    vec2 off1 = vec2(1.411764705882353) * direction;
    vec2 off2 = vec2(3.2941176470588234) * direction;
    vec2 off3 = vec2(5.176470588235294) * direction;
    color += texture2D(image, uv) * 0.1964825501511404;
    color += texture2D(image, uv + (off1 / resolution)) * 0.2969069646728344;
    color += texture2D(image, uv - (off1 / resolution)) * 0.2969069646728344;
    color += texture2D(image, uv + (off2 / resolution)) * 0.09447039785044732;
    color += texture2D(image, uv - (off2 / resolution)) * 0.09447039785044732;
    color += texture2D(image, uv + (off3 / resolution)) * 0.010381362401148057;
    color += texture2D(image, uv - (off3 / resolution)) * 0.010381362401148057;
    return color;
}

void main(){
    gl_FragColor = blur13(texture0, TexCoords, vec2(400, 300), vec2(1.0, 0.0));
}

I assume you have swapped pIdBlur and pId .我假设您已经交换了pIdBlurpId

I' will give you introductions for gaussian blur shader with 2 passes.我将向您介绍具有 2 个通道的高斯模糊着色器。 This is an approximation which first blurs along the X-Axis in the 1st pass and along the Y-Axis in the 2nd pass.这是一个近似值,它首先在第一遍中沿 X 轴模糊,在第二遍中沿 Y 轴模糊。 This results in a better performance for strong blurring.这导致更好的强模糊性能。 The blur shader uses a normal (or Gaussian) distribution .模糊着色器使用正态(或高斯)分布 For the 2 passes is used the same shader program, with individual direction settings for the 2 passes, stored in the uniform vec2 u_dir .对于 2 个通道,使用相同的着色器程序,2 个通道的单独方向设置存储在统一的vec2 u_dir The strength of the blur effect can be varied with the uniform variable float u_sigma in the range [0.0, 1.0].模糊效果的强度可以在 [0.0, 1.0] 范围内随统一变量float u_sigma变化。

Blur Vertex shader模糊顶点着色器

precision mediump float;
attribute vec2 inPos;
varying   vec2 pos;

void main()
{
    pos = inPos;
    gl_Position = vec4( inPos, 0.0, 1.0 );
}

Blur Fragment shader模糊片段着色器

precision mediump float;
varying vec2 pos;

uniform sampler2D u_texture;
uniform vec2      u_textureSize;
uniform float     u_sigma;
uniform vec2      u_dir;

float CalcGauss( float x, float sigma )
{
    if ( sigma <= 0.0 )
        return 0.0;
    return exp( -(x*x) / (2.0 * sigma) ) / (2.0 * 3.14157 * sigma);
}

void main()
{
    vec2 texC     = pos.st * 0.5 + 0.5;
    vec4 texCol   = texture2D( u_texture, texC );
    vec4 gaussCol = vec4( texCol.rgb, 1.0 );
    vec2 step     = u_dir / u_textureSize;
    for ( int i = 1; i <= 32; ++ i )
    {
        float weight = CalcGauss( float(i) / 32.0, u_sigma * 0.5 );
        if ( weight < 1.0/255.0 )
            break;
        texCol    = texture2D( u_texture, texC + step * float(i) );
        gaussCol += vec4( texCol.rgb * weight, weight );
        texCol    = texture2D( u_texture, texC - step * float(i) );
        gaussCol += vec4( texCol.rgb * weight, weight );
    }
    gaussCol.rgb = clamp( gaussCol.rgb / gaussCol.w, 0.0, 1.0 );
    gl_FragColor = vec4( gaussCol.rgb, 1.0 );
}

After the program has been linked the uniform locations and attribute indices can be read from:程序链接后,可以从以下位置读取统一位置和属性索引:

GLint attrInxPos = glGetAttribLocation( pIdBlur, "inPos" );
GLint locTexture = glGetUniformLocation( pIdBlur, "u_texture" );
GLint locTexSize = glGetUniformLocation( pIdBlur, "u_textureSize" );
GLint locSigma   = glGetUniformLocation( pIdBlur, "u_sigma" );
GLint locDir     = glGetUniformLocation( pIdBlur, "u_dir" );

A vertex array object, containing a quad, which later will be drawn over the whole viewport, for a screen space blur pass, has to be created:必须创建一个顶点数组对象,其中包含一个四边形,稍后将在整个视口上绘制,用于屏幕空间模糊传递:

GLuint screenVAO;
glGenVertexArrays( 1, &screenVAO );
glBindVertexArray( screenVAO );
GLuint quadBuf;
glGenBuffers( 1, &quadBuf );
glBindBuffer( GL_ARRAY_BUFFER, quadBuf );
GLfloat screenRect[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f };
glBufferData( GL_ARRAY_BUFFER, 8 * sizeof( float ), screenRect, GL_STATIC_DRAW );
glEnableVertexAttribArray( attrInxPos );
glVertexAttribPointer( attrInxPos, 2, GL_FLOAT, GL_FALSE, 0, nullptr );

2 frame buffers, with a texture attached to its color plane, have to be created.必须创建 2 个帧缓冲区,纹理附加到其颜色平面。 In the 1st one the scene is drawn.在第一个场景中绘制。 The 2nd one is used by the 1st blur pass.第二个用于第一个模糊通道。 The 2nd blur pass draws directly to the drawing buffer.第二个模糊通道直接绘制到绘图缓冲区。

GLuint texObj[2];
GLuint fbObj[2];
glGenTextures(2, texObj);
glGenFramebuffers(2, fbObj);
glActiveTexture(GL_TEXTURE0);
for ( int i = 0; i < 2; i ++ )
{
    glBindTexture(GL_TEXTURE_2D, texObj[i]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glBindFramebuffer(GL_FRAMEBUFFER, fbObj[i]);
    glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texObj[i], 0 );
    GLuint renderbuffer;
    glGenRenderbuffers(1, &renderbuffer);
    glBindRenderbuffer( GL_RENDERBUFFER, renderbuffer );
    glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height );
    glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer );
}
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

Now everything what is needed for the blur passes has been generated.现在已经生成了模糊通道所需的一切。

To draw and blur the scene the following steps have to be applied.要绘制和模糊场景,必须应用以下步骤。 First you have to bind and clear the 1st frame buffer首先,您必须绑定并清除第一个帧缓冲区

glBindFramebuffer(GL_FRAMEBUFFER, fbObj[0]);
glClearColor(0.0f, 0.0f, 0.4f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

and use the shader program for drawing the objects:使用着色器程序绘制对象:

glUseProgram(pId);

Now draw the object(s) of the scene.现在绘制场景的对象。

.....
glDrawArrays(GL_TRIANGLES, 0, 3);

The second step is the 1st blur pass.第二步是第一个模糊通道。 The blur program has to be use and the 2nd framebuffer has to be bound.必须使用模糊程序并且必须绑定第二个帧缓冲区。 After the frame 1st buffer has been released, you can use the texture, that is attached to its color plane, as an input for the blur shader.释放第 1 帧缓冲区后,您可以使用附加到其颜色平面的纹理作为模糊着色器的输入。
Note, a texture can't be source and destination at the same time, this would cause undefined behavior.请注意,纹理不能同时是源和目标,这会导致未定义的行为。
To bind the texture to the shader, you have to bind the texture to a texture unit and assign the index of the texture unit to the uniform sampler of the shader.要将纹理绑定到着色器,您必须将纹理绑定到纹理单元,并将纹理单元的索引分配给着色器的均匀采样器。

int texUnitIndex = 1;
GLfloat texSize  = { width, height };
GLfloat dirX[]   = { 1.0f, 0.0f };  
GLfloat sigma    = .....; // 0.0 <= sigma <= 1.0

glBindFramebuffer(GL_FRAMEBUFFER, fbObj[1]);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(pIdBlur);
glActiveTexture(GL_TEXTURE0 + texUnitIndex);
glBindTexture(GL_TEXTURE_2D, texObj[0]);
glUniform1i(locTexture, texUnitIndex); 
glUniform2fv(locTexSize, texSize);
glUniform2fv(locTexSize, dirX);
glUniform1f(locTexSize, sigma);

To apply the blur pass a quad has to be drawn of the viewport area.要应用模糊通道,必须在视口区域中绘制四边形。

glBindVertexArray( screenVAO );
glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );

The 2nd and final blur pass, is similar to the 1st blur pass.第二个也是最后一个模糊通道,类似于第一个模糊通道。 The target texture of the 1st blur pass is the source texture, and the target is the drawing buffer.第一个模糊通道的目标纹理是源纹理,目标是绘图缓冲区。 The blur direction has to be set up for the Y axis of the viewport.必须为视口的 Y 轴设置模糊方向。

GLfloat dirY[] = { 0.0f, 1.0f }; 

glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, texObj[1]);
glUniform2fv(locTexSize, dirY);

See also the answers to the following question:另请参阅以下问题的答案:

See alos a similar WebGL example:另请参阅类似的 WebGL 示例:

 (function loadscene() { var resize, gl, progDraw, progBlur, vp_size, blurFB; var canvas, camera, bufCube = {}, bufQuad = {}; var shininess = 10.0, glow = 10.0, sigma = 0.8, radius = 1.0; function render(deltaMS){ var sliderScale = 100; sigma = document.getElementById( "sigma" ).value / sliderScale; radius = document.getElementById( "radius" ).value / sliderScale; vp_size = [canvas.width, canvas.height]; camera.Update( vp_size ); gl.enable( gl.DEPTH_TEST ); gl.clearColor( 0.0, 0.0, 0.0, 1.0 ); gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT ); // set up framebuffer gl.bindFramebuffer( gl.FRAMEBUFFER, blurFB[0] ); gl.viewport( 0, 0, blurFB[0].width, blurFB[0].height ); gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT ); // setup view projection and model var prjMat = camera.Perspective(); var viewMat = camera.LookAt(); var modelMat = RotateAxis( IdentM44(), Fract( deltaMS / 13000.0 ) * 2.0 * Math.PI, 0 ); modelMat = RotateAxis( modelMat, Fract( deltaMS / 17000.0 ) * 2.0 * Math.PI, 1 ); // set up draw shader ShProg.Use( progDraw ); ShProg.SetM44( progDraw, "u_projectionMat44", prjMat ); ShProg.SetM44( progDraw, "u_modelViewMat44", Multiply(viewMat, modelMat) ); ShProg.SetF1( progDraw, "u_shininess", shininess ); // draw scene VertexBuffer.Draw( bufCube ); // set blur-X framebuffer and bind frambuffer texture gl.bindFramebuffer( gl.FRAMEBUFFER, blurFB[1] ); gl.viewport( 0, 0, blurFB[1].width, blurFB[1].height ); gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT ); var texUnit = 1; gl.activeTexture( gl.TEXTURE0 + texUnit ); gl.bindTexture( gl.TEXTURE_2D, blurFB[0].color0_texture ); // set up blur-X shader ShProg.Use( progBlur ); ShProg.SetI1( progBlur, "u_texture", texUnit ) ShProg.SetF2( progBlur, "u_textureSize", vp_size ); ShProg.SetF1( progBlur, "u_sigma", sigma ) ShProg.SetF1( progBlur, "u_radius", radius ) ShProg.SetF2( progBlur, "u_dir", [1.0, 0.0] ) // draw full screen space gl.enableVertexAttribArray( progBlur.inPos ); gl.bindBuffer( gl.ARRAY_BUFFER, bufQuad.pos ); gl.vertexAttribPointer( progBlur.inPos, 2, gl.FLOAT, false, 0, 0 ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufQuad.inx ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); gl.disableVertexAttribArray( progBlur.inPos ); // reset framebuffer and bind frambuffer texture gl.bindFramebuffer( gl.FRAMEBUFFER, null ); gl.viewport( 0, 0, vp_size[0], vp_size[1] ); gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT ); texUnit = 2; gl.activeTexture( gl.TEXTURE0 + texUnit ); gl.bindTexture( gl.TEXTURE_2D, blurFB[1].color0_texture ); // set up pst process shader ShProg.SetI1( progBlur, "u_texture", texUnit ) ShProg.SetF1( progBlur, "u_radius", radius ) ShProg.SetF2( progBlur, "u_dir", [0.0, 1.0] ) // draw full screen space gl.enableVertexAttribArray( progBlur.inPos ); gl.bindBuffer( gl.ARRAY_BUFFER, bufQuad.pos ); gl.vertexAttribPointer( progBlur.inPos, 2, gl.FLOAT, false, 0, 0 ); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufQuad.inx ); gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); gl.disableVertexAttribArray( progBlur.inPos ); requestAnimationFrame(render); } function initScene() { canvas = document.getElementById( "canvas"); gl = canvas.getContext( "experimental-webgl" ); if ( !gl ) return null; progDraw = ShProg.Create( [ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER }, { source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER } ] ); if ( !progDraw.progObj ) return null; progDraw.inPos = gl.getAttribLocation( progDraw.progObj, "inPos" ); progDraw.inNV = gl.getAttribLocation( progDraw.progObj, "inNV" ); progDraw.inCol = gl.getAttribLocation( progDraw.progObj, "inCol" ); progBlur = ShProg.Create( [ { source : "post-shader-vs", stage : gl.VERTEX_SHADER }, { source : "blur-shader-fs", stage : gl.FRAGMENT_SHADER } ] ); progBlur.inPos = gl.getAttribLocation( progBlur.progObj, "inPos" ); if ( !progBlur.progObj ) return; // create cube var cubePos = [ -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0 ]; var cubeCol = [ 1.0, 0.0, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ]; var cubeHlpInx = [ 0, 1, 2, 3, 1, 5, 6, 2, 5, 4, 7, 6, 4, 0, 3, 7, 3, 2, 6, 7, 1, 0, 4, 5 ]; var cubePosData = []; for ( var i = 0; i < cubeHlpInx.length; ++ i ) { cubePosData.push( cubePos[cubeHlpInx[i]*3], cubePos[cubeHlpInx[i]*3+1], cubePos[cubeHlpInx[i]*3+2] ); } var cubeNVData = []; for ( var i1 = 0; i1 < cubeHlpInx.length; i1 += 4 ) { var nv = [0, 0, 0]; for ( i2 = 0; i2 < 4; ++ i2 ) { var i = i1 + i2; nv[0] += cubePosData[i*3]; nv[1] += cubePosData[i*3+1]; nv[2] += cubePosData[i*3+2]; } for ( i2 = 0; i2 < 4; ++ i2 ) cubeNVData.push( nv[0], nv[1], nv[2] ); } var cubeColData = []; for ( var is = 0; is < 6; ++ is ) { for ( var ip = 0; ip < 4; ++ ip ) { cubeColData.push( cubeCol[is*3], cubeCol[is*3+1], cubeCol[is*3+2] ); } } var cubeInxData = []; for ( var i = 0; i < cubeHlpInx.length; i += 4 ) { cubeInxData.push( i, i+1, i+2, i, i+2, i+3 ); } bufCube = VertexBuffer.Create( [ { data : cubePosData, attrSize : 3, attrLoc : progDraw.inPos }, { data : cubeNVData, attrSize : 3, attrLoc : progDraw.inNV }, { data : cubeColData, attrSize : 3, attrLoc : progDraw.inCol } ], cubeInxData ); bufQuad.pos = gl.createBuffer(); gl.bindBuffer( gl.ARRAY_BUFFER, bufQuad.pos ); gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( [ -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0 ] ), gl.STATIC_DRAW ); bufQuad.inx = gl.createBuffer(); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufQuad.inx ); gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ), gl.STATIC_DRAW ); camera = new Camera( [0, 3, 0.0], [0, 0, 0], [0, 0, 1], 90, vp_size, 0.5, 100 ); window.onresize = resize; resize(); requestAnimationFrame(render); } function resize() { //vp_size = [gl.drawingBufferWidth, gl.drawingBufferHeight]; vp_size = [window.innerWidth, window.innerHeight] //vp_size = [256, 256] canvas.width = vp_size[0]; canvas.height = vp_size[1]; var fbsize = Math.max(vp_size[0], vp_size[1]); fbsize = 1 << 31 - Math.clz32(fbsize); // nearest power of 2 blurFB = []; for ( var i = 0; i < 2; ++ i ) { fb = gl.createFramebuffer(); fb.width = fbsize; fb.height = fbsize; gl.bindFramebuffer( gl.FRAMEBUFFER, fb ); fb.color0_texture = gl.createTexture(); gl.bindTexture( gl.TEXTURE_2D, fb.color0_texture ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, fb.width, fb.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); fb.renderbuffer = gl.createRenderbuffer(); gl.bindRenderbuffer( gl.RENDERBUFFER, fb.renderbuffer ); gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, fb.width, fb.height ); gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, fb.color0_texture, 0 ); gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, fb.renderbuffer ); gl.bindTexture( gl.TEXTURE_2D, null ); gl.bindRenderbuffer( gl.RENDERBUFFER, null ); gl.bindFramebuffer( gl.FRAMEBUFFER, null ); blurFB.push( fb ); } } function Fract( val ) { return val - Math.trunc( val ); } function CalcAng( deltaTime, intervall ) { return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI; } function CalcMove( deltaTime, intervall, range ) { var pos = self.Fract( deltaTime / (1000*intervall) ) * 2.0 var pos = pos < 1.0 ? pos : (2.0-pos) return range[0] + (range[1] - range[0]) * pos; } function EllipticalPosition( a, b, angRag ) { var a_b = a * a - b * b var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b ); var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b ); return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ]; } function IdentM44() { return [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]; }; function RotateAxis(matA, angRad, axis) { var aMap = [ [1, 2], [2, 0], [0, 1] ]; var a0 = aMap[axis][0], a1 = aMap[axis][1]; var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad); var matB = matA.slice(0); for ( var i = 0; i < 3; ++ i ) { matB[a0*4+i] = matA[a0*4+i] * cosAng + matA[a1*4+i] * sinAng; matB[a1*4+i] = matA[a0*4+i] * -sinAng + matA[a1*4+i] * cosAng; } return matB; } function Rotate(matA, angRad, axis) { var s = Math.sin(angRad), c = Math.cos(angRad); var x = axis[0], y = axis[1], z = axis[2]; matB = [ x*x*(1-c)+c, x*y*(1-c)-z*s, x*z*(1-c)+y*s, 0, y*x*(1-c)+z*s, y*y*(1-c)+c, y*z*(1-c)-x*s, 0, z*x*(1-c)-y*s, z*y*(1-c)+x*s, z*z*(1-c)+c, 0, 0, 0, 0, 1 ]; return Multiply(matA, matB); } function Multiply(matA, matB) { matC = IdentM44(); for (var i0=0; i0<4; ++i0 ) for (var i1=0; i1<4; ++i1 ) matC[i0*4+i1] = matB[i0*4+0] * matA[0*4+i1] + matB[i0*4+1] * matA[1*4+i1] + matB[i0*4+2] * matA[2*4+i1] + matB[i0*4+3] * matA[3*4+i1] return matC; } function Cross( a, b ) { return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0 ]; } function Dot( a, b ) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; } function Normalize( v ) { var len = Math.sqrt( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] ); return [ v[0] / len, v[1] / len, v[2] / len ]; } Camera = function( pos, target, up, fov_y, vp, near, far ) { this.Time = function() { return Date.now(); } this.pos = pos; this.target = target; this.up = up; this.fov_y = fov_y; this.vp = vp; this.near = near; this.far = far; this.Perspective = function() { var n = this.near; var f = this.far; var fn = f + n; var f_n = f - n; var r = this.vp[0] / this.vp[1]; var t = 1 / Math.tan( Math.PI * this.fov_y / 360 ); return [ t/r, 0, 0, 0, 0, t, 0, 0, 0, 0, -fn/f_n, -1, 0, 0, -2*f*n/f_n, 0 ]; }; this.LookAt = function() { var mz = Normalize( [ this.pos[0]-this.target[0], this.pos[1]-this.target[1], this.pos[2]-this.target[2] ] ); var mx = Normalize( Cross( this.up, mz ) ); var my = Normalize( Cross( mz, mx ) ); var tx = Dot( mx, this.pos ); var ty = Dot( my, this.pos ); var tz = Dot( [-mz[0], -mz[1], -mz[2]], this.pos ); return [mx[0], my[0], mz[0], 0, mx[1], my[1], mz[1], 0, mx[2], my[2], mz[2], 0, tx, ty, tz, 1]; }; this.Update = function(vp_size) { if (vp_size) this.vp = vp_size; }; } var ShProg = { Create: function (shaderList) { var shaderObjs = []; for (var i_sh = 0; i_sh < shaderList.length; ++i_sh) { var shderObj = this.Compile(shaderList[i_sh].source, shaderList[i_sh].stage); if (shderObj) shaderObjs.push(shderObj); } var prog = {} prog.progObj = this.Link(shaderObjs) if (prog.progObj) { prog.attrInx = {}; var noOfAttributes = gl.getProgramParameter(prog.progObj, gl.ACTIVE_ATTRIBUTES); for (var i_n = 0; i_n < noOfAttributes; ++i_n) { var name = gl.getActiveAttrib(prog.progObj, i_n).name; prog.attrInx[name] = gl.getAttribLocation(prog.progObj, name); } prog.uniLoc = {}; var noOfUniforms = gl.getProgramParameter(prog.progObj, gl.ACTIVE_UNIFORMS); for (var i_n = 0; i_n < noOfUniforms; ++i_n) { var name = gl.getActiveUniform(prog.progObj, i_n).name; prog.uniLoc[name] = gl.getUniformLocation(prog.progObj, name); } } return prog; }, AttrI: function (prog, name) { return prog.attrInx[name]; }, UniformL: function (prog, name) { return prog.uniLoc[name]; }, Use: function (prog) { gl.useProgram(prog.progObj); }, SetI1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1i(prog.uniLoc[name], val); }, SetF1: function (prog, name, val) { if (prog.uniLoc[name]) gl.uniform1f(prog.uniLoc[name], val); }, SetF2: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform2fv(prog.uniLoc[name], arr); }, SetF3: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform3fv(prog.uniLoc[name], arr); }, SetF4: function (prog, name, arr) { if (prog.uniLoc[name]) gl.uniform4fv(prog.uniLoc[name], arr); }, SetM33: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix3fv(prog.uniLoc[name], false, mat); }, SetM44: function (prog, name, mat) { if (prog.uniLoc[name]) gl.uniformMatrix4fv(prog.uniLoc[name], false, mat); }, Compile: function (source, shaderStage) { var shaderScript = document.getElementById(source); if (shaderScript) source = shaderScript.text; var shaderObj = gl.createShader(shaderStage); gl.shaderSource(shaderObj, source); gl.compileShader(shaderObj); var status = gl.getShaderParameter(shaderObj, gl.COMPILE_STATUS); if (!status) alert(gl.getShaderInfoLog(shaderObj)); return status ? shaderObj : null; }, Link: function (shaderObjs) { var prog = gl.createProgram(); for (var i_sh = 0; i_sh < shaderObjs.length; ++i_sh) gl.attachShader(prog, shaderObjs[i_sh]); gl.linkProgram(prog); status = gl.getProgramParameter(prog, gl.LINK_STATUS); if ( !status ) alert(gl.getProgramInfoLog(prog)); return status ? prog : null; } }; var VertexBuffer = { Create: function(attribs, indices, type) { var buffer = { buf: [], attr: [], inx: gl.createBuffer(), inxLen: indices.length, primitive_type: type ? type : gl.TRIANGLES }; for (var i=0; i<attribs.length; ++i) { buffer.buf.push(gl.createBuffer()); buffer.attr.push({ size : attribs[i].attrSize, loc : attribs[i].attrLoc, no_of: attribs[i].data.length/attribs[i].attrSize }); gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buf[i]); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( attribs[i].data ), gl.STATIC_DRAW); } gl.bindBuffer(gl.ARRAY_BUFFER, null); if ( buffer.inxLen > 0 ) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.inx); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); } return buffer; }, Draw: function(bufObj) { for (var i=0; i<bufObj.buf.length; ++i) { gl.bindBuffer(gl.ARRAY_BUFFER, bufObj.buf[i]); gl.vertexAttribPointer(bufObj.attr[i].loc, bufObj.attr[i].size, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray( bufObj.attr[i].loc); } if ( bufObj.inxLen > 0 ) { gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx); gl.drawElements(bufObj.primitive_type, bufObj.inxLen, gl.UNSIGNED_SHORT, 0); gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null ); } else gl.drawArrays(bufObj.primitive_type, 0, bufObj.attr[0].no_of ); for (var i=0; i<bufObj.buf.length; ++i) gl.disableVertexAttribArray(bufObj.attr[i].loc); gl.bindBuffer( gl.ARRAY_BUFFER, null ); } }; initScene(); })();
 html,body { margin: 0; overflow: hidden; } #gui { position : absolute; top : 0; left : 0; }
 <script id="draw-shader-vs" type="x-shader/x-vertex"> precision mediump float; attribute vec3 inPos; attribute vec3 inNV; attribute vec3 inCol; varying vec3 vertPos; varying vec3 vertNV; varying vec3 vertCol; uniform mat4 u_projectionMat44; uniform mat4 u_modelViewMat44; void main() { vertNV = mat3( u_modelViewMat44 ) * normalize( inNV ); vertCol = inCol; vec4 pos = u_modelViewMat44 * vec4( inPos, 1.0 ); vertPos = pos.xyz / pos.w; gl_Position = u_projectionMat44 * pos; } </script> <script id="draw-shader-fs" type="x-shader/x-fragment"> precision mediump float; varying vec3 vertPos; varying vec3 vertNV; varying vec3 vertCol; uniform float u_shininess; void main() { vec3 color = vertCol; vec3 normalV = normalize( vertNV ); vec3 eyeV = normalize( -vertPos ); vec3 halfV = normalize( eyeV + normalV ); float NdotH = max( 0.0, dot( normalV, halfV ) ); float shineFac = ( u_shininess + 2.0 ) * pow( NdotH, u_shininess ) / ( 2.0 * 3.14159265 ); gl_FragColor = vec4( color.rgb * (0.2 + NdotH), 1.0 ); } </script> <script id="post-shader-vs" type="x-shader/x-vertex"> precision mediump float; attribute vec2 inPos; varying vec2 pos; void main() { pos = inPos; gl_Position = vec4( inPos, 0.0, 1.0 ); } </script> <script id="blur-shader-fs" type="x-shader/x-fragment"> precision mediump float; varying vec2 pos; uniform sampler2D u_texture; uniform vec2 u_textureSize; uniform float u_sigma; uniform float u_radius; uniform vec2 u_dir; float CalcGauss( float x, float sigma ) { if ( sigma <= 0.0 ) return 0.0; return exp( -(x*x) / (2.0 * sigma) ) / (2.0 * 3.14157 * sigma); } void main() { vec2 texC = pos.st * 0.5 + 0.5; vec4 texCol = texture2D( u_texture, texC ); vec4 gaussCol = vec4( texCol.rgb, 1.0 ); vec2 step = u_dir / u_textureSize; for ( int i = 1; i <= 32; ++ i ) { float weight = CalcGauss( float(i) / 32.0, u_sigma * 0.5 ); if ( weight < 1.0/255.0 ) break; texCol = texture2D( u_texture, texC + u_radius * step * float(i) ); gaussCol += vec4( texCol.rgb * weight, weight ); texCol = texture2D( u_texture, texC - u_radius * step * float(i) ); gaussCol += vec4( texCol.rgb * weight, weight ); } gaussCol.rgb = clamp( gaussCol.rgb / gaussCol.w, 0.0, 1.0 ); gl_FragColor = vec4( gaussCol.rgb, 1.0 ); } </script> <div> <form id="gui" name="inputs"> <table> <tr> <td> <font color= #CCF>radius</font> </td> <td> <input type="range" id="radius" min="1" max="1000" value="200"/></td> </tr> <tr> <td> <font color= #CCF>blur</font> </td> <td> <input type="range" id="sigma" min="1" max="50" value="10"/></td> </tr> </table> </form> </div> <canvas id="canvas" style="border: none;"></canvas>

In general you need to draw the scene you want to blur to a frame buffer object (FBO) with attached texture.通常,您需要将要模糊的场景绘制到带有附加纹理的帧缓冲对象 (FBO)。

  • Create a frame buffer创建帧缓冲区
  • Create an empty texture (data parameter should be null)创建一个空纹理(数据参数应为空)
  • Bind frame buffer and texture绑定帧缓冲区和纹理
  • Attach the texture to frame buffer as color将纹理作为颜色附加到帧缓冲区

At this point the rest of the drawing should be exactly the same as on your main buffer but make sure you setup a correct viewport.此时,绘图的其余部分应该与您的主缓冲区完全相同,但请确保您设置了正确的视口。 This procedure will make you draw to the texture.此过程将使您绘制纹理。

Now that you have a texture with your scene you need to go to the same procedure as is for blurring an image.现在您的场景有了纹理,您需要执行与模糊图像相同的过程。

  • Bind your main buffer (usually indexed 0)绑定主缓冲区(通常索引为 0)
  • Bind texture绑定纹理
  • Draw the texture to main buffer with blur shader使用模糊着色器将纹理绘制到主缓冲区

You can then optimize it with horizontal and vertical blur shaders having 2 calls which uses yet another FBO...然后,您可以使用具有 2 个调用的水平和垂直模糊着色器对其进行优化,这些调用使用另一个 FBO ...

So I would try doing some steps in your application:所以我会尝试在您的应用程序中执行一些步骤:

  • Create a scene which draws and shows a triangle创建一个绘制并显示三角形的场景
  • Create a FBO, draw to it and draw the FBO texture on your main buffer创建一个 FBO,绘制它并在主缓冲区上绘制 FBO 纹理
  • Create a scene which draws and shows a blurred image through texture创建一个场景,通过纹理绘制和显示模糊图像
  • Create a FBO on which the scene is drawn then draws a blurred texture from FBO on the main buffer创建一个在其上绘制场景的 FBO,然后在主缓冲区上从 FBO 绘制模糊纹理

If you find yourself in trouble at any of these points you might want to ask a specific question about it.如果您发现自己在任何这些方面遇到麻烦,您可能想提出一个具体的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM