webgl渲染管线、缓冲区绘制三角形

本文介绍了WebGL的基本概念及其渲染管线流程,并通过绘制一个简单的三角形来演示如何使用WebGL进行3D图形开发。


前言

作为gis专业的前端开发人员,工作中少不了与三维场景打交道,像cesium、threejs、mapbox-gl等前端渲染库底层都是封装了WebGL,本文以从一个基础的三角形开始入门WebGL的学习


一、WebGL是什么

WebGL 是一组基于 JavaScript 语言的图形规范,浏览器厂商按照这组规范进行实现,为 Web 开发者提供一套3D图形相关的 API。通过JavaScript和GLSL语言对电脑的显卡(GPU)进行操作,从而在屏幕上绘制出各种各样的3D应用。

二、WebGL渲染管线

不同与传统的前端开发,WebGL程序虽然也使用JavaScrip进行数据的传递与开发,但是却需要GLSL语言实现与GPU的通信(GPU是不认JavaScript的),为了将三维数据显示在浏览器的屏幕上,WebGL有一套固定的流程,这个流程称为渲染管线。
渲染管线大致可以分为以下几个步骤:

1 获取坐标元素

由JavaScript处理着色器需要的顶点坐标、颜色、纹理等信息,并负责为着色器提供这些数据。

数据的获取由JavaScript代码提供,这些步骤由CPU完成,然后通过webgl的相关方法将这些数据传递给着色器程序,由GPU运行着色器代码

2 顶点着色器

Webgl实际是控制GPU的渲染,GPU上运行的代码是一对着色器:顶点着色器和片元着色器,着色器在运行时会依次调用顶点着色器和片元着色器。

顶点着色器接收 JavaScript 传递过来的与顶点相关的信息,将顶点绘制到对应坐标,顶点着色器的赋值一般是通过以下这种形式,其中xyz是由webgl借助自己的方法从JavaScript取到的:

 gl_Position = vec4 (x, y, z, 1);

gl_Position 接收一个 4 维向量表示的坐标,即(X, Y ,Z ,W),W 不等于 0,这个坐标是在裁剪坐标系中,我们称它为裁剪坐标,裁剪坐标W一般情况赋1就可以。他的原点在屏幕中心,X轴正方向向右,Y轴正方向向上,范围都是[-1,1]。

顶点着色器中既可以接受JavaScript提供的信息,又可以将与顶点相关的信息进行一定变换后传递给片元着色器。顶点着色器在数据的传递可以通过以下几个关键字:

关键字 描述
attributes 获取缓冲区内的数据信息,只能由外部传递给顶点着色器
uniform 一般用来传递全局信息
varyings 将顶点信息传递给片元
textures 获取纹理信息

3 图元装配

图元装配指的是将设置的点、纹理、颜色组装为可渲染多边形的过程。比如将三个顶点装配成三角形图元,顶点着色器执行了三次,形成了一个三角形图元。

图元有可能是一个点,一个线段,三角形或者其他多边形,,具体图元类型取决于在调用绘制函数时选择的图形类型。

4 光栅化阶段

光栅化阶段指将装配好的图元进行像素填充,同时在这个过程中可能还会将一些已经装配好但是不在屏幕显示范围内(遮挡、放大溢出屏幕、背面)的部分裁剪掉以提高WebGL的运行效率。

5 片元着色器

,光栅化阶段后的每个被填充过的像素都称为片元,在片元着色器阶段,GPU为片元内部的像素z逐一填充颜色信息。其运行的次数由图形的片元数决定 。

步骤3 4对于开发者来说是无法直接控制的,WebGL会自动帮助完成这些工作

三、 绘制一个三角形

1 初始化一个HTML

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<title>webgl</title>
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<style>
		body {
     
     
			margin: 0;
			padding:<
WebGL 本身并不直接支持“德劳内三角剖分”(Delaunay Triangulation),它只是一个底层图形 API,用于在浏览器中渲染 2D/3D 图形。要使用 **WebGL 结合德劳内三角剖分绘制三角形**,你需要: 1. 使用算法或库对一组点进行 **德劳内三角剖分**; 2. 将生成的三角形顶点数据传入 WebGL 渲染管线; 3. 使用 WebGL 绘制这些三角形。 下面是一个完整的示例:使用 JavaScript 库 [d3-delaunay](https://github.com/d3/d3-delaunay) 进行德劳内三角剖分,并通过 WebGL 渲染结果。 --- ### ✅ 示例:使用 Delaunay 三角剖分 + WebGL 绘制三角形 ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Delaunay + WebGL Triangle</title> <style> body { margin: 0; overflow: hidden; } canvas { display: block; } </style> </head> <body> <canvas id="glCanvas"></canvas> <script src="https://cdn.jsdelivr.net/npm/d3-delaunay@6"></script> <script> // 主程序 function main() { const canvas = document.getElementById('glCanvas'); const gl = canvas.getContext('webgl'); if (!gl) { alert('WebGL not supported!'); return; } // 设置画布尺寸为窗口大小 canvas.width = window.innerWidth; canvas.height = window.innerHeight; gl.viewport(0, 0, canvas.width, canvas.height); // 创建着色器程序 const program = createProgram(gl); gl.useProgram(program); // 生成随机点(用于 Delaunay 三角剖分) const numPoints = 50; const points = []; for (let i = 0; i < numPoints; i++) { points.push( Math.random() * canvas.width, Math.random() * canvas.height ); } // 使用 d3-delaunay 进行三角剖分 const delaunay = d3.Delaunay.from(points); const indices = delaunay.triangles; // Uint32Array,每三个索引构成一个三角形 // 转换坐标到 WebGL 的 NDC(归一化设备坐标)范围 [-1, 1] const positions = new Float32Array(points); for (let i = 0; i < positions.length; i += 2) { positions[i] = (positions[i] / canvas.width ) * 2 - 1; // x → [-1, 1] positions[i + 1] = (positions[i + 1] / canvas.height) * 2 - 1; // y → [-1, 1] } // 创建顶点缓冲区 const positionBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); // 获取 attribute 变量位置 const aPositionLocation = gl.getAttribLocation(program, 'a_position'); gl.enableVertexAttribArray(aPositionLocation); gl.vertexAttribPointer(aPositionLocation, 2, gl.FLOAT, false, 0, 0); // 清屏 gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); // 使用索引绘制三角形(TRIANGLES) gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_INT, 0); } // 创建着色器程序的辅助函数 function createShader(gl, type, source) { const shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { console.error('Shader compile error:', gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; } function createProgram(gl) { const vertexSource = ` attribute vec2 a_position; void main() { gl_Position = vec4(a_position, 0.0, 1.0); } `; const fragmentSource = ` precision mediump float; void main() { gl_FragColor = vec4(1.0, 0.7, 0.3, 1.0); // 橙色 } `; const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexSource); const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentSource); const program = gl.createProgram(); gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { console.error('Program link error:', gl.getProgramInfoLog(program)); return null; } return program; } // 启动主程序 window.onload = main; </script> </body> </html> ``` --- ### 🔍 解释 1. **d3-delaunay**: - 我们引入了 `d3-delaunay` 库来处理点集的德劳内三角剖分。 - `delaunay.triangles` 返回的是索引数组,表示哪些顶点组成三角形。 2. **坐标转换**: - 原始点是像素坐标 `(0 ~ width, 0 ~ height)`。 - WebGL 使用 NDC(Normalized Device Coordinates)范围 `[-1, 1]`,所以需要映射。 3. **WebGL 流程**: - 编写着色器(顶点 + 片段); - 创建缓冲区并上传顶点数据; - 启用属性指针; - 调用 `drawElements` 使用索引绘制三角形。 4. **性能优化建议**: - 对于大量点,可以考虑使用 `Uint16Array` 并改用 `gl.UNSIGNED_SHORT`; - 若点动态变化,应使用 `gl.DYNAMIC_DRAW`。 --- ### ✅ 效果 运行此代码后,你会看到由 Delaunay 三角剖分生成的一组连接成网状的三角形,填充整个屏幕区域,颜色为橙色。 --- ### 🧩 注意事项 - `d3-delaunay` 是轻量级、无依赖的,适合仅做三角剖分; - 如果你想可视化边框或交互式编辑点,可结合 HTML5 Canvas 或 Three.js; - WebGL 不支持自动拓扑分析,所有几何结构必须提前计算好。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值