WebGL入门-基本概念
WebGL(Web Graphics Library)是一种3D绘图标准,它允许JavaScript和OpenGL ES 2.0相结合,为HTML5 Canvas提供硬件3D加速渲染。
推荐使用NSDT 3DConvert进行3D模型格式转换,支持glb、obj、stp、fbx、ifc等多种3D模型格式之间进行互相转换,在转换过程中,能够很好的保留模型原有的颜色、材质等信息。
webGL 使用 canvas
标签进行开发,扩展了web复杂图形渲染的三维层次。
示例:画点
使用webGL画一个点,主要有以下几个步骤:
- 创建一个含有 标签的 HTML
- 创建一个用于编写webGL程序的JS
- 创建顶点着色器
- 创建片元着色器
- 融合顶点和片元着色器
- 清除背景
- 画出图形
代码如下:
<!Doctype html>
<html>
<head>
<title> web-gl example </title>
</head>
<body onload="main()">
<canvas id="web-gl-example" width="400" height="400" />
<script src="./main.js" />
</body>
</html>
function initShader(gl, p, f) {}
/**
* @todos
* 1.获取canvas标签对象
* 2.获取web-gl-context对象
* 3.编写着色器
* 4.清空背景
* 5.画在背景上
*/
function main() {
const canvas = document.getElementById("web-gl-example");
const gl = canvas.getContext("web-gl");
/* 顶点着色器:设置大小、位置 */
const pointShader = `
void main() {
gl_PointSize = 10.0; // 像素大小
gl_Position = vec4(0.0, 0.0, 0.0, 1.0); // vector4 四维齐次坐标
// x,y,z,w => (x/w, y/w, z/w)
}
`
/* 片元着色器:描述像素的特征 */
const fragShader = `
void main() {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); // RGBA格式颜色
}
`
/* 错误判断; initShader() 是融合两个着色器的方法 */
if (initShader(gl, pointShader, fragShader) === null)
throw new Error("着色器编写出错!");
// 清空背景
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.Color_Buffer_Bit); // 全大写
/*
* api描述:多种功能的画图API,根据传参的不同而绘画形式不同
*/
gl.drawArrays(gl.Point, 0, 1); // 全大写
}
坐标系
webgl 的坐标系是右手坐标系,即:X轴平行与屏幕上下边沿,Y轴平行于屏幕左右边沿,Z轴垂直与屏幕向外延申,原点位于canvas的中心。简略坐标朝向如图所示:
其他
缓冲区
webgl的缓冲区是用来存放展示信息的内存块,当我们绘画、清除的时候会使用他们。通常web-gl包含三种缓冲区:
- 颜色缓冲区
- 深度缓冲区
- 模板缓冲区
着色器 -- shader
着色器是web-gl的核心,它包含了图形中像素的绘画信息。web-gl拥有两种着色器:
- 顶点着色器:描述点的信息:大小、位置等
- 片元着色器:描述片元/像素(fragment) 的信息,颜色、光照等
外部动态参数
使用attribute/uniform关键字进行传递参数到着色器程序中,使着色器函数内部变量随外部动态变化进行交互。 API 参考:
gl.getAttributeLocation(program, name);
// 获取Attribute变量的位置gl.vertexAttrib3f(location, ...args);
// 给内部Attribute变量赋值- ... // 赋值函数可以进行参数数目的扩展,从 1 - 4 个。
gl.getUniformLocation(program, name);
// 获取Uniform变量的地址gl.uniform3f(location, ...args);
- ... // 与
attribute
类似
参考代码如下:
function main() {
... // 获取 canvas + web-gl 对象
const pointShader = `
attribute vec4 p_Position; // <描述> <类型> <变量名>
void main() {
gl_Position = p_Position;
gl_PointSize = 10.0;
}
`;
... // 片元着色器 + 融合着色器方法
// 获取 p_Position 的位置
const p_Position = gl.getAttributeLocation(gl.program, 'p_Position');
// 赋值
gl.vertexAttrib3f(p_Position, 0.0, 0.0, 0.0);
... // 继续清除背景+画点
}
注意:Attribute 和 Uniform 的差别是:Attribute 变量是单个顶点的特征;Uniform 变量是所有点的共同特征,编程层面上基本类似。后面还会出现varying, 这里暂且不谈。
坐标转换
在用户交互过程中,用户点击事件触发在 Client 上面,导致在获取变化位置时要进行坐标转换。从屏幕坐标到canvas坐标到web-gl坐标。如图:
需要从左上角的屏幕原点,转到canvas的左上原点:
let x = clientX - canvas.top;
let y = clientY - canvas.left;
再从canvas转换到webgl:
x = (canvas.width / 2) - x; // 横向中心点
y = -((canvas.height / 2) - y) = y - (canvas.height / 2); // 纵向中心点,更改坐标轴方向
最后单位化:
x /= (canvas.width / 2);
y /= (canvas.height / 2);
多个点的绘制
多个点的绘制主要依赖以下步骤:
- 使用数组记录点的位置
- 清空背景
- 遍历数组,依次画出点
代码如下:
// gl 的初始化,shader的初始化
const points = [[0, 0], [0.5, 0.5], [-0.5, 0.5]];
const pointPosition = gl.getAttributeLocation(gl.program, 'PointPosition');
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear();
for (let i = 0; i < points.length; i++) {
gl.vertexAttrib3f(pointPosition, points[i][0], points[i][1], 0.0);
gl.drawArrays(gl.Points, 0, 1);
}