18.1WebGL 简洁教程
优质
小牛编辑
142浏览
2023-12-01
什么是WebGL
虽然WebGL可以用来实现3D对象绘制,但WebGL并非一个3D引擎
WebGL只是关于绘制点、线、三角形的接口
它是在GPU上运行代码的低级API
着色器(Shaders)
使用着色器(shader)来绘制图形
顶点着色器计算顶点位置
片段着色器处理光栅化(像素点颜色)
GL着色器语言
看起来如何?
特定于GPU的语言 (GLSL) 看起来像C语言,以void main()开始 但是内置了用来处理2D/3D渲染的数据类型和函数顶点着色器代码
attribute vec3 position; void main() { gl_Position = vec4(position, 1.0); }
着色器通过位置(position)属性从缓冲区获取数据
对位置缓冲区中的每个位置运行着色器
顶点位置通过gl_position
设置
片段着色器代码
precision highp float;
void main() {
vec2 p = gl_FragCoord.xy;
gl_FragColor = vec4(1.0, 0.5, 0, 1.0);
}
对每个片段(像素)运行片段着色器
像素坐标可以从gl_FragCoord
读取
输出颜色以gl_FragColor
设置
从JS传递数据
attribute: 顶点着色器从缓冲区中提取一个值并将其存储在此处
uniform: 在执行着色器之前,传递在JS中设置的值,这些值对于绘制调用中的所有顶点保持不变
varying: 变量(Varyings)是一种将值从顶点着色器传递到片段着色器的方法。和常量(Uniforms)不同的是,该值可以被顶点着色器修改,然后在片段着色器中读取(只读)。变量的值是一个插值数据,每个像素都不同。
在线示例:绘制一个渐变色三角形
GL着色器语言
数据类型
primitives (bool, int, float) vectors (vec2, vec3, vec4) matrices (mat2, mat3, mat4) texture data (sampler2D)常用内置函数
三角函数 sin, cos, atan 线性插值 (mix) 矢量运算 Vector arithmetics (+, -, *, /, dot, cross, length) 矩阵运算 Matrix arithmetics (+, -, *)编写JavaScript
获取WebGL上下文
const gl = canvas.getContext('webgl')
和2d canvas上下文类似
编译着色器代码
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentCode);
gl.compileShader();
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(fragmentShader, vertexCode);
gl.compileShader();
和c语言类似,我们需要先把高级语言编译为GPU认识的机器语言。
创建程序
const program = gl.createProgram();
program.attachShader(vertexShader);
program.attachShader(fragmentShader);
program.linkProgram();
也和C语言类似,这两个着色器代码被链接进一个WebGLProgram。
你可以用program.validateProgram()来检查程序是否有效
为顶点着色器定义属性(attributes)
const positionLoc = this.gl.getAttribLocation(program, 'position');
this.gl.enableVertexAttribArray(positionLoc);
通过enableVertexAttribArray来启用属性
创建缓存
// provide 2D data for a triangle
const data = [-1, -1, -1, 1, 1, -1];
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
创建一个缓存并通过Float32Array提供数据
设置属性(attribute)指针
const recordSize = 2;
const stride = 0; // 0 = advance through the buffer by recordSize * sizeof(data type)
const offset = 0; // the starting point in the buffer
const type = gl.FLOAT; // data type
const normalized = false; // normalize the data (unused for gl.FLOAT)
gl.vertexAttribPointer(positionLoc, recordSize, type, normalized, stride, offset);
把缓存赋给属性
传递常量(uniform)变量
const uTime = gl.getUniformLocation(program, 'time');
gl.uniform1f(loc, tickCount);
可能的类型:floats, ints, bools, vectors, matrices
从JavaScript传递变量给WebGL,比如屏幕分辨率、时间、鼠标位置以及一些控制参数等。
绘制
createBuffers()
setAttributes();
function animLoop(time = 0) {
setUniforms();
gl.drawArrays(gl.TRIANGLES);
requestAnimationFrame(animLoop);
}
animLoop();
Useful GLSL functions
// normalize coords and set (0, 0) to center
vec2 coords() {
float vmin = min(width, height);
return vec2((gl_FragCoord.x - width * .5) / vmin,
(gl_FragCoord.y - height * .5) / vmin);
}
//rotate
vec2 rotate(vec2 p, float a) {
return vec2(p.x * cos(a) - p.y * sin(a),
p.x * sin(a) + p.y * cos(a));
}
//repeat
vec2 repeat(in vec2 p, in vec2 c) {
return mod(p, c) - 0.5 * c;
}