「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。
本篇是Three.js
源码解读的开篇,这一系列文章笔者争取做到用最形象的比喻,最合适的图示,让读者学会使用webgl绘制一个奇幻的3d世界。
WebGLRenderer
WebGLRenderer这个类是Three.js对webgl渲染管线的一个封装,也就是webgl如何渲染一个3d界面的封装。其代码的文件结构如下:
由上图可以得出两个个结论:
- shaders用于存储着色器代码。
- webgl文件夹用于存储js控制代码。 webgl要渲染一个3d世界,需要js控制代码和着色器代码去配合完成。
js控制代码的示例如下:
var program = gl.createProgram();
// 添加预先存在的着色器
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
着色器代码示例如下:
export const vertex = /* glsl */`
varying vec2 vUv;
uniform mat3 uvTransform;
void main() {
vUv = ( uvTransform * vec3( uv, 1 ) ).xy;
gl_Position = vec4( position.xy, 1.0, 1.0 );
}
`;
export const fragment = /* glsl */`
uniform sampler2D t2D;
varying vec2 vUv;
void main() {
vec4 texColor = texture2D( t2D, vUv );
gl_FragColor = mapTexelToLinear( texColor );
#include <tonemapping_fragment>
#include <encodings_fragment>
};
初学者如果看到上述js
控制代码和着色器代码有点陌生没有关系。这次源码系列笔者将会力争做到用最形象的比喻让你理解webgl
的技术原理和three.js
的哲学思想。
WebGLProgram是什么?
已经清楚WebGLProgram是什么的同学可以跳过这一部分,直接看源码分析。
WebGLProgram 是 WebGL API 的一部分,它由两个
WebGLShader
s (webgl 着色器)组成,分别为顶点着色器和片元着色器(两种着色器都是采用 GLSL 语言编写的)。
着色器程序在使用的过程中要分为几步,包括告知 GPU 来使用这段着色器程序,绑定合适的数据缓冲区,配置相关选项,最终把图像绘制到屏幕上。
如果3d世界是一个画家创造的,WebGLProgram
就像画家手中的画笔。WebGLRenderingContext
就是画家的手,GPU
相当于画家的大脑。
画家(GPU)拥有如何绘制3d世界的数据,通过手(WebGLRenderingContext
)去操纵画笔(WebGLProgram
)将3d世界绘制出来,这个画笔可以蘸上不同的颜色,调整为不同的粗细将3d世界绘制出来。
WebGLRenderContext
提供了四个方法去操作WebGLProgram
。
- createProgram,创建一个着色器程序。
- linkProgram,将着色器程序与渲染上下文绑定。
- isProgram,是否是一个程序对象。
- deleteProgram,释放该着色器的内存。
画家一但配置好了画笔,通过双手调用画笔的不同功能,就能开始工作了。
webgl
本身提供了WebGLProgram
这个类,three.js
对WebGLProgram
又做了一个进一步的封装。
下列代码是three.js
对WebGLProgram
的封装,读者可以先自行理解,也可直接跳到源码解读界面:
function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {}
three.js
中WebGLProgram
的入参
- renderer,WebGLRenderer的实例。
- cacheKey,缓存key,推测用于性能优化。
- parameters,参数,推测用于创建program时的比如顶点数据,着色器数据。
- bindingStates,绑定状态,当前程序已经绘制出了怎样的界面所需要的数据状态。
renderer
const gl = renderer.getContext();
这里renderer
的作用是获取这个渲染器的渲染上下文实例(WebGLRenderingContext
)。
cacheKey
目前在构造函数未发现作用。
parameters
包含了顶点着色器代码和片元着色器代码,同时包含了着色器代码的具体内容配置,用以生成着色器代码。
具体内容配置实在太多,我们挑几个出来讲解一下。
webgl的版本信息
const customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters );
webgl2
使用OpenGL ES
3.0,webgl
使用OpenGL ES
2.0。
OpenGL ES
3.0`推出了更多的新特性。
- 优化性能
- 统一纹理格式ETC。
- 支持更多强大的功能比如渲染多重纹理。
着色器语言版本信息
着色器语言跟js语言一样,在不停地迭代版本。
let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : '';
比如,你想使用glsl语言的300版本,可以在着色器代码首行声明如下:
#version 300 es
如果你想检测浏览器所支持的glsl语言版本,可以如下方式获取:
gl.getParameter(gl.SHADING_LANGUAGE_VERSION);
define的作用
parameters.instancing ? '#define USE_INSTANCING' : '',
#define USE_INSTANCING
'#ifdef USE_INSTANCING',
'attribute mat4 instanceMatrix;',
通过上述代码,可以看出,代码配置参数用来动态生成着色器代码段。
着色器代码配置参数可以说是对glsl
着色器语言语法的一个详细说明,弄懂每个参数的含义,你会深入glsl
语言和图形学的更多细节。
bindingStates
WebGLBindingStates
的实例。WebGLBindingStates
是Three.js
封装的一个类。
WebGLProgram构造函数返回值
this.usedTimes = 1;
this.program = program;
this.vertexShader = glVertexShader;
this.fragmentShader = glFragmentShader;
return this;
挂载了着色器程序对象以及着色器程序对象所包含的顶点着色器对象和片元着色器对象。
看了three.js
对WebGLProgram
的封装之后,我想说api文档原来是这样出来的。
更多的细节读者可以自行去阅读WebGLProgram的源码,感谢读者的阅读!