WebGL着色器和Three.js自己定义后期处理着色器
为了渲染出一个网格的最终图像,开发者需要准确的定义顶点、变换、材质、光源以及相机是如何相互作用并最终生成图像的。而承担这个工作的就是着色器(shader)。着色器又称作可编程着色器,它是一段源码,它实现了将网格像素点投影到屏幕上的算法。图形硬件能够解析顶点、纹理以及其他底层的东西,但是并不能处理材质、光源、变换以及相机。这些高级的结构由着色器程序来处理。
着色器通常使用类C的高级语言编写,并被编译成可以被图形处理单元(GPU)执行的代码。所以着色器是运行在GPU上的程序。
创建着色器的时候需要使用OpenGL着色器语言(OpenGL Shading language,简称GLSL)。
相关网站:
WebGL规范: www.khronos.org/webgl
着色器资源网站:Shadertoy
着色器的数据类型
类型 | 描述 |
---|---|
void | 表示一个空值 |
bool | 接受true或false |
int | 这是一个有符号整数数据类型 |
float | 这是一个浮点标量数据类型 |
vec2, vec3, vec4 | 正分量浮点矢量 |
bvec2, bvec3, bvec4 | 布尔矢量 |
ivec2, ivec3, ivec4 | 有符号整数矢量 |
mat2, mat3, mat4 | 2x2, 3x3, 4x4 浮点矩阵 |
sampler2D | 访问2D纹理 |
samplerCube | 访问立方体映射纹理 |
修饰符
修饰符 | 描述 |
---|---|
attribute | 这个修饰符充当每个顶点数据的顶点着色器和OpenGL ES之间的链接。此顶点着色器属性的值在每次执行时变化 |
uniform | 这修饰符链接着色器程序及其WebGL的应用程序。不同属性修饰词,制服(uniforms)的值不会改变。制服(uniforms)是只读的; 可以用它们与任何基本数据类型来声明一个变量。 |
varying | 这个修饰符形成顶点着色器的内插数据和片段着色器之间的联系。它可用于下列数据类型- float, vec2, vec3, vec4, mat2, mat3, mat4, 或数组。 |
three.js官方自带的shader
通过THREE.ShaderPass通道可以传入一个自定义的着色器,通过这种方式来实现自定义的着色器效果。在官方文档中提供了48个自定义的shader:
名称 | 描述 | 名称 | 描述 |
---|---|---|---|
AfterimageShader.js | BasicShader.js | ||
BleachBypassShader.js | BlendShader.js | ||
BokehShader.js | BokehShader2.js | ||
BrightnessContrastShader.js | ColorCorrectionShader.js | 调整颜色分布 | |
ColorifyShader.js | ConvolutionShader.js | ||
CopyShader.js | 不添加任何效果,只是将最后一个通道的输出结果复制到屏幕上,用处很多 | DepthLimitedBlurShader.js | |
DigitalGlitch.js | DOFMipMapShader.js | ||
DotScreenShader.js | FilmShader.js | ||
FocusShader.js | FreiChenShader.js | ||
FresnelShader.js | FXAAShader.js | ||
GammaCorrectionShader.js | HalftoneShader.js | ||
HorizontalBlurShader.js | HorizontalTiltShiftShader.js | ||
HueSaturationShader.js | 改变颜色的色调和饱和度 | KaleidoShader.js | |
LuminosityHighPassShader.js | LuminosityShader.js | ||
MirrorShader.js | 可以为部分屏幕创建镜面效果 | NormalMapShader.js | |
OceanShaders.js | ParallaxShader.js | ||
PixelShader.js | RGBShiftShader.js | ||
SAOShader.js | SepiaShader.js | ||
SMAAShader.js | SobelOperatorShader.js | ||
SSAOShader.js | TechnicolorShader.js | ||
ToneMapShader.js | TriangleBlurShader.js | ||
UnpackDepthRGBAShader.js | VerticalBlurShader.js | ||
VerticalTiltShiftShader.js | VignetteShader.js | 添加晕映效果,可以在图片中央周围显示黑色边框 | |
VolumeShader.js | WaterRefractionShader.js |
上面的shader可以直接放在shaderpass中使用,下面演示创建一个自己定义的灰度着色器。
three.js自定义灰度着色器
下面是自定义的shader代码:
THREE.CustomGrayScaleShader = {
uniforms: {
"tDiffuse": {type: "t", value: null},
"rPower": {type: "f", value: 0.2126},
"gPower": {type: "f", value: 0.7152},
"bPower": {type: "f", value: 0.0722}
},
// 0.2126 R + 0.7152 G + 0.0722 B 图像灰度化加权平均法
// 对于后期处理步骤,vertexshader始终是相同的
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"uniform float rPower;",
"uniform float gPower;",
"uniform float bPower;",
"uniform sampler2D tDiffuse;",
"varying vec2 vUv;",
"void main() {",
// 获取对应顶点的像素
"vec4 texel = texture2D( tDiffuse, vUv );",
// 计算灰度值
"float gray = texel.r*rPower + texel.g*gPower + texel.b*bPower;",
// 返回灰度化的顶点颜色
"gl_FragColor = vec4( vec3(gray), texel.w );",
"}"
].join("\n")
};
上面的代码采用GLSL语言写的,有点类似于C语言。其原理是采用:图像灰度化中的加权平均法
要创建自定义的着色器,需要实现两个部分,顶点着色器(vertexSh)和片元着色器(fragmentShader)。顶点着色器用于改变顶点的位置,片元着色器用于定义每个像素的颜色。对于后期处理,我们只需要改变片元着色器就可以,然后使用默认的顶点着色器。
代码中的uv,它表示的是Texel(纹理上的像素对应的坐标),该值会通过“varying vec2 vUv”变量传递到片元着色器。tDiffuse是纹理,它是THREE.EffectComppser组合器中前一个通道处理的结果。
片元着色器的作用是在传递过来的纹理上获取正确的像素。实现的方式是调用texture2D方法,第一个参数是获取的纹理,第二个参数是顶点着色器传递来的uv坐标。通过该方法就获取了每个顶点的像素值,包含rgbw(红绿蓝透明度)四个参数。然后根据加权平均法计算出灰度值,将该点的颜色变成该灰度。这样整个画面就变成了灰度图了。vec4( vec3(gray), texel.w )返回的就是每个彩色点的灰度值。整个过程解释起来有点困难,多回味一下就懂了。
配置THREE.RffectComposer。需要引入一些必要的js文件。
<script type="text/javascript" src="./js/postprocessing/EffectComposer.js"></script>
<script type="text/javascript" src="./js/postprocessing/RenderPass.js"></script>
<script type="text/javascript" src="./js/postprocessing/ShaderPass.js"></script>
<script type="text/javascript" src="./js/shaders/CopyShader.js"></script>
主要代码:
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xffffff);
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);//setSize一定要放在前面,否则场景会模糊
var compose = new THREE.EffectComposer(renderer);
var renderPass = new THREE.RenderPass(scene, camera);
var effectCopy = new THREE.ShaderPass(THREE.CopyShader);
effectCopy.renderToScreen = true;
var shaderPass = new THREE.ShaderPass(THREE.CustomGrayScaleShader);
compose.addPass(renderPass);
compose.addPass(shaderPass);
compose.addPass(effectCopy);
我理解为,通道类似于PS的图层。每个图层都有自己的处理效果,比如二值化、增加饱和度、反转、高斯模糊等操作,其中shaderposs通道类似一个特殊的图层,可以自定义后期效果。
PS的图层都可以叠加显示,但是three.js中有的通道不能输出到屏幕,需要借助copyshader来实现,copyshader又是定义在shaderposs通道上。上面自己定义的灰度化shader也是定义到该通道上。