WebGL上下文是从canvas标签中获取到的,通过canvas对象的getContext()函数可以获取WebGLRenderingContext。
跟据参数的不同,getContext()函数可以获取不同种类的渲染上下文。
当你调用 canvas.getContext('webgl')
时,返回的是一个 WebGLRenderingContext 对象。这个对象是 WebGL API 的核心接口,它提供了用于与 WebGL 进行交互的方法和属性。
一、基本使用
WebGLRenderingContext
对象的方法和属性主要用于以下几类操作:
- 绘制操作:
clear()
: 清除指定的缓冲区。drawArrays()
: 使用顶点数组绘制图元。drawElements()
: 使用索引数组绘制图元。
- 缓冲区操作:
createBuffer()
: 创建新的缓冲区对象。bindBuffer()
: 绑定缓冲区对象。bufferData()
: 创建并初始化缓冲区对象的数据存储。
- 着色器操作:
createShader()
: 创建新的着色器对象。shaderSource()
: 设置着色器源代码。compileShader()
: 编译着色器代码。createProgram()
: 创建新的着色器程序。attachShader()
: 将着色器附加到程序上。linkProgram()
: 链接着色器程序。useProgram()
: 使用指定的着色器程序。
- 纹理操作:
createTexture()
: 创建新的纹理对象。bindTexture()
: 绑定纹理对象。texImage2D()
: 指定二维纹理图像。
- 属性和视图设置:
viewport()
: 设置视口。enable()
: 启用特定的 WebGL 功能。disable()
: 禁用特定的 WebGL 功能。
- 获取信息:
getParameter()
: 返回特定参数的值。getProgramInfoLog()
: 获取着色器程序的错误信息。getShaderInfoLog()
: 获取着色器的错误信息。
// 获取 WebGL 上下文
var canvas = document.getElementById('myCanvas');
var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) {
console.log('WebGL not supported, falling back on experimental-webgl');
gl = canvas.getContext('experimental-webgl');
}
if (!gl) {
alert('Your browser does not support WebGL');
}
// 设置视口
gl.viewport(0, 0, canvas.width, canvas.height);
// 清除颜色缓冲区
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// 创建和编译顶点着色器
var vertexShaderSource = `
attribute vec4 a_Position;
void main() {
gl_Position = a_Position;
}
`;
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
// 创建和编译片段着色器
var fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // Red color
}
`;
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
// 链接着色器程序
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
// 创建缓冲区并传递顶点数据
var vertices = new Float32Array([
0.0, 0.5,
-0.5, -0.5,
0.5, -0.5
]);
var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 获取顶点着色器中的位置变量并启用它
var a_Position = gl.getAttribLocation(shaderProgram, 'a_Position');
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Position);
// 绘制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);
二、webgl坐标系
WebGL 的默认坐标系是一个右手坐标系,范围在 [-1, 1] 之间。具体来说:
- X 轴:从左到右,范围是 -1 到 1。
- Y 轴:从下到上,范围是 -1 到 1。
- Z 轴:从屏幕外到屏幕内,范围是 -1 到 1。
在 WebGL 中,顶点坐标通常使用归一化设备坐标(NDC),这些坐标经过投影和视口变换后映射到屏幕坐标。
归一化设备坐标(NDC, Normalized Device Coordinates)中的 -1 并不表示无穷远。NDC 是一种线性坐标系,用于表示顶点在裁剪空间中的位置,范围是从 -1 到 1。这个坐标系是线性的,而不是对数的。
详细说明:
- NDC 范围:
- X 轴:从 -1 到 1,表示从屏幕的左边缘到右边缘。
- Y 轴:从 -1 到 1,表示从屏幕的下边缘到上边缘。
- Z 轴:从 -1 到 1,表示从近裁剪面(near plane)到远裁剪面(far plane)。
- 线性坐标:
- NDC 是线性的,这意味着在 NDC 范围内的每个单位长度是等距的。例如,从 -1 到 0 的距离与从 0 到 1 的距离是相同的。
- 在 WebGL 中,通过投影矩阵将视图空间(摄像机空间)坐标转换为裁剪空间坐标,然后进行透视除法(perspective divide)得到 NDC 坐标。
- 透视投影与深度缓冲:
- 透视投影矩阵将视图空间中的坐标转换为裁剪空间坐标。这一过程中,深度值(Z 值)会被非线性地压缩,以便在近裁剪面附近有更高的精度。
- 然后,通过透视除法,将裁剪空间坐标转换为 NDC 坐标。此时,NDC 坐标中的 Z 值仍然是线性的,范围在 -1 到 1 之间。
- 最后,NDC 坐标会被映射到屏幕空间坐标,并存储在深度缓冲区中。深度缓冲区中的值通常是非线性的,以便更好地处理透视投影的深度精度问题。
Three.js 坐标系
Three.js 使用右手坐标系,默认情况下:
- X 轴:从左到右。
- Y 轴:从下到上。
- Z 轴:从屏幕外到屏幕内(正 Z 轴指向屏幕外,负 Z 轴指向屏幕内)。
Three.js 的世界坐标系和 WebGL 的 NDC 坐标系不同。Three.js 提供了相机和投影矩阵来将三维世界坐标转换为 WebGL 的 NDC 坐标。
CesiumJS 坐标系
CesiumJS 是用于地理空间应用的 3D 引擎,它使用右手坐标系,但与其他引擎有所不同:
- X 轴:指向本初子午线(0度经线)方向。
- Y 轴:指向东经90度方向。
- Z 轴:指向北极方向。
CesiumJS 主要使用地理坐标(经度、纬度、高度)和地心坐标(ECEF,Earth-Centered, Earth-Fixed),并提供转换函数将它们转换为 WebGL 的 NDC 坐标。
Babylon.js 坐标系
Babylon.js 也是一个使用右手坐标系的 3D 引擎,默认情况下:
- X 轴:从左到右。
- Y 轴:从下到上。
- Z 轴:从屏幕外到屏幕内(正 Z 轴指向屏幕外,负 Z 轴指向屏幕内)。
与 Three.js 类似,Babylon.js 使用相机和投影矩阵将三维世界坐标转换为 WebGL 的 NDC 坐标。
总结
- WebGL:右手坐标系,NDC 范围为 [-1, 1]。
- Three.js:右手坐标系,默认相机方向与 WebGL 一致。
- CesiumJS:右手坐标系,地理空间坐标系与地心坐标系。
- Babylon.js:右手坐标系,默认相机方向与 WebGL 一致。