如何通过后期处理通道增强实现图像效果的疑问?

摘要:GPU是并行渲染的,这样的渲染很高效。但是在实际需求中,有时我们计算片元色值时,需要依赖周围像素点或者某个其他位置像素点的颜色信息,这样的话想要一次性完成绘制就无法做到,需要对纹理进行二次加工处理。
前言 大家好,本文分享的是如何使用后期处理通道增强图像效果,通过前面几篇文章,我们了解了一些动态生成纹理的方法,比如符号距离场SDF、基于参数方程生成图案、基于噪声生成纹理,等等。这些生成纹理的技术有相似的地方,就是根据片元的纹理坐标,对片元着色,直接生成纹理。 因为GPU是并行渲染的,每个像素的着色器程序是并行执行的,这样的渲染很高效。但是在实际需求中,有时我们计算片元色值时,需要依赖周围像素点或者某个其他位置像素点的颜色信息,这样的话想要一次性完成绘制就无法做到了。我们需要先通过第一次的绘制,来得到动态生成的纹理,接着我们才能根据纹理坐标获取到这个纹理上任一位置的颜色信息,再做后续处理。也就是我们至少要执行两次处理,才能实现我们最终想要的效果。 那么具体要怎么做呢,下面我就用一个高斯模糊的例子来进行演示。 高斯模糊的例子 假设我们通过以下Shader代码绘制了随机的三角形图案。 const fragment = ` #ifdef GL_ES precision highp float; #endif varying vec2 vUv; ${distance.base} ${noise.random2d} ${color.hsb} void main() { vec2 st = vUv; st *= 10.0; vec2 i_st = floor(st); vec2 f_st = 2.0 * fract(st) - vec2(1); float r = random(i_st); float sign = 2.0 * step(0.5, r) - 1.0; float d = triangle_distance( f_st, vec2(-1), vec2(1), sign * vec2(1, -1) ); gl_FragColor.rgb = (smoothstep(-0.85, -0.6, d) - smoothstep(0.0, 0.05, d)) * hsb2rgb(vec3(r + 1.2, 0.5, r)); gl_FragColor.a = 1.0; } `; 以上就是动态生成的纹理,在生成的过程中我们无法直接给纹理添加高斯模糊的滤镜。 为了使用这个第一次渲染的结果,我们需要准备一个新的片元着色器。 ## blurFragment #ifdef GL_ES precision highp float; #endif varying vec2 vUv; uniform sampler2D tMap; void main() { vec4 color = texture2D(tMap, vUv); gl_FragColor.rgb = color.rgb; gl_FragColor.a = color.a; } 这里的变量tMap就是第一次渲染生成的纹理。那么我们要怎么获取这个纹理呢?这就要用到WebGL中的帧缓冲对象,Frame Buffer Object。 当我们没有绑定帧缓冲对象时,Shader生成的图形会使用默认的缓冲区,直接输出绘制到画布上,当然这样我们是拿不到渲染结果的,这里为了对渲染结果二次加工,我们需要在执行渲染前绑定帧缓冲对象,这样在渲染时就会实现类似OffscreenCanvas的离屏绘制,将渲染结果输出到帧缓冲对象中。 const fbo = renderer.createFBO(); // 创建帧缓冲对象 renderer.bindFBO(fbo); // 绑定,指定输出到的帧缓冲对象 renderer.render(); // 输出到帧缓冲对象 renderer.bindFBO(null); // 解除绑定 const blurProgram = renderer.compileSync(blurFragment, vertex); renderer.useProgram(blurProgram); renderer.setMeshData(program.meshData); renderer.uniforms.tMap = fbo.texture; // 将前一个着色器程序生成的纹理作为新着色器的 tMap 变量 renderer.render(); 在完成输出后,就解除绑定,并使用新的片元着色器创建一个新的着色器程序,并开启使用。 此时我们可以通过fbo.texture获取到前一个着色器程序生成的纹理,并传递给新的着色器使用。
阅读全文