如何通过后期处理通道增强实现图像效果的疑问?
摘要: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获取到前一个着色器程序生成的纹理,并传递给新的着色器使用。
