webGL硬核知识:图形渲染管渲染流程,各个阶段对应的API调用方式
一、图形渲染管线基础流程概述
WebGL 的图形渲染管线大致可分为以下几个主要阶段,每个阶段都有其特定的任务,协同工作将 3D 场景中的物体最终转换为屏幕上呈现的 2D 图像:

- 顶点处理(Vertex Processing)阶段:
-
- 该阶段主要处理传入的顶点数据,包括顶点坐标、顶点颜色、法线向量等顶点相关的属性信息。进行的操作有坐标变换(例如将顶点从模型坐标系转换到世界坐标系、再到视图坐标系、裁剪坐标系等)以及对顶点属性进行必要的设置和计算等。
- 例如,将一个 3D 模型中各个顶点的初始坐标通过平移、旋转、缩放等矩阵变换,使其放置到正确的空间位置,并且根据光照等需求计算顶点的光照属性(如果在顶点着色器中处理光照部分)。
- 图元装配(Primitive Assembly)阶段:
-
- 把经过顶点处理后的顶点按照指定的绘制模式(如
gl.TRIANGLES、gl.TRIANGLE_STRIP等)组合成基本图元,像三角形、线段等,这些基本图元是后续光栅化的基础单元。例如,根据传入的顶点数据,按照绘制三角形的模式将 3 个顶点装配成一个三角形图元。
- 把经过顶点处理后的顶点按照指定的绘制模式(如
- 光栅化(Rasterization)阶段:
-
- 这个阶段将图元转换为屏幕上对应的像素片段(Fragment),也就是确定哪些像素会被图元覆盖,计算出每个像素对应的坐标等信息,它是从几何图形到离散像素的转换过程。例如,一个三角形图元经过光栅化后,会确定其在屏幕空间中覆盖了哪些具体的像素位置。
- 片段处理(Fragment Processing)阶段:
-
- 对光栅化生成的每个像素片段进行处理,主要是确定每个片段的最终颜色、透明度等属性,会涉及到纹理采样(如果有纹理映射)、光照计算(若在片段着色器中处理光照)、颜色混合等操作,最终决定该片段在屏幕上显示的样子。比如,根据纹理坐标从绑定的纹理图像中获取相应的颜色值,结合光照计算结果等来确定该像素最终呈现的颜色。
- 帧缓冲(Framebuffer)阶段:
-
- 帧缓冲可以看作是一个存储渲染结果的区域,片段处理后的像素颜色等信息会被写入到帧缓冲中对应的位置,然后最终由浏览器将帧缓冲中的内容显示到屏幕上,完成整个渲染过程。例如,渲染的一帧图像数据先存放在帧缓冲里,再根据显示设置展示在网页的画布区域。

二、各阶段对应的 WebGL API 调用方式
顶点处理阶段
- 创建缓冲区对象(Buffer Object)并绑定:
-
- 首先使用
gl.createBuffer()函数创建一个缓冲区对象,这个缓冲区用于存储顶点数据(如顶点坐标、颜色等)。例如:
const buffer = gl.createBuffer();
- 首先使用
-
然后通过
gl.bindBuffer()函数将创建好的缓冲区绑定到特定的目标上,常见的目标有gl.ARRAY_BUFFER(用于存储顶点数组数据,比如顶点坐标、颜色等属性数组)和gl.ELEMENT_ARRAY_BUFFER(用于存储索引数据,在使用索引绘制时用到)。比如绑定用于存储顶点坐标数据的缓冲区:gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
-
向缓冲区填充数据:
使用gl.bufferData()函数将实际的顶点数据填充到绑定的缓冲区中,需要指定数据的类型(如gl.STATIC_DRAW表示数据不会或很少改变,gl.DYNAMIC_DRAW表示数据会较频繁改变等)以及具体的数据内容(通常是一个类型化数组,如Float32Array)。例如,填充顶点坐标数据:const vertices = new Float32Array([
// 这里是一系列顶点坐标值,比如一个三角形的三个顶点坐标
0.0, 0.5, 0.0,
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0
]);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); -
创建并编译着色器(Shader):
顶点处理主要通过顶点着色器来实现,需要创建顶点着色器对象,使用gl.createShader(gl.VERTEX_SHADER)创建,然后通过gl.shaderSource()函数设置着色器的源代码(用 GLSL 语言编写),最后用gl.compileShader()函数编译着色器。例如:const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const vertexShaderSource =attribute vec4 a_position; void main() { gl_Position = a_position; };
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
这里的attribute变量用于接收从外部传入的顶点属性数据(如顶点坐标),gl_Position是内置的输出变量,用于将处理后的顶点坐标传递给下一个阶段。
4. 创建着色器程序(Program)并链接:
创建一个着色器程序对象,将编译好的顶点着色器(以及后续的片段着色器,如果有的话)添加到程序中,然后通过gl.linkProgram()函数链接它们,使其成为一个完整可执行的程序。例如:
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
// 假设已经创建并编译好片段着色器fragmentShader,也添加进来
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
-
获取属性位置并启用顶点属性数组:
在链接好的程序中,通过gl.getAttribLocation()函数获取顶点属性变量(如a_position)对应的位置索引,然后使用gl.enableVertexAttribArray()函数启用该顶点属性数组,以便后续传递数据给顶点着色器进行处理。例如:const aPositionLocation = gl.getAttribLocation(program, ‘a_position’);
gl.enableVertexAttribArray(aPositionLocation); -
指定顶点属性数据的读取方式:
使用gl.vertexAttribPointer()函数来指定顶点属性数据在缓冲区中的读取方式,包括数据的类型、每个顶点属性的分量数量、是否需要归一化、步长(相邻顶点属性数据之间的间隔字节数)以及起始偏移量等信息。例如:gl.vertexAttribPointer(
aPositionLocation, // 顶点属性位置索引
3, // 每个顶点属性的分量数量(这里是3个坐标值,x、y、z)
gl.FLOAT, // 数据类型为浮点数
false, // 不需要归一化
0, // 步长为0,表示紧密排列
0 // 起始偏移量为0
);
图元装配阶段
在 WebGL 中,图元装配阶段主要通过指定绘制模式来进行,使用gl.drawArrays()或gl.drawElements()函数来触发图元的装配和绘制操作,同时也隐含了图元装配这个过程。
-
**gl.drawArrays()**函数调用方式:
它用于直接根据缓冲区中的顶点数据按照指定的绘制模式来绘制图元,语法如下:gl.drawArrays(mode, first, count);
其中,mode参数指定绘制模式,常见的值有gl.TRIANGLES(绘制独立的三角形)、gl.TRIANGLE_STRIP(绘制三角形带,可节省顶点数据)、gl.LINES(绘制线段)等;first表示从缓冲区中的哪个顶点开始绘制;count表示要绘制的顶点数量。例如,用gl.TRIANGLES模式绘制一个简单的三角形:
gl.drawArrays(gl.TRIANGLES, 0, 3);
-
**gl.drawElements()**函数调用方式:
当使用索引数据来指定图元的顶点顺序时,使用该函数,语法如下:gl.drawElements(mode, count, type, offset);
mode同样是绘制模式;count是要绘制的索引数量;type是索引数据的类型(如gl.UNSIGNED_SHORT等);offset是索引数据在索引缓冲区中的偏移量(字节数)。例如,假设有索引数据存储在另一个缓冲区中,通过索引绘制三角形:
// 假设已经创建并绑定好索引缓冲区,填充好索引数据
const indices = new Uint16Array([0, 1, 2]);
gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_SHORT, 0);
光栅化阶段
光栅化阶段在 WebGL 中是自动进行的,由 GPU 根据前面图元装配阶段确定的图元信息来执行,开发者一般不需要直接调用特定的 API 来干预这个过程,但可以通过一些设置间接影响,比如设置视口(Viewport)大小,通过gl.viewport()函数来指定渲染结果在屏幕上显示的区域范围,语法如下:
gl.viewport(x, y, width, height);
其中,x、y是视口在屏幕中的起始坐标(通常以左下角为原点),width和height分别是视口的宽度和高度。例如,设置整个画布区域为视口:
const canvas = document.getElementById('webglCanvas');
gl.viewport(0, 0, canvas.width, canvas.height);
片段处理阶段
- 片段着色器编写与相关操作(类似顶点着色器部分步骤):
-
- 创建片段着色器对象,使用
gl.createShader(gl.FRAGMENT_SHADER),设置源代码(用 GLSL 编写,主要用于确定片段的颜色等属性),然后编译它,例如:
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
const fragmentShaderSource =precision mediump float; void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 设置片段颜色为红色 };
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader); - 创建片段着色器对象,使用
这里gl_FragColor是内置的输出变量,用于指定该片段最终的颜色值(这里设置为红色)。
- 将编译好的片段着色器添加到之前创建的着色器程序中,并链接程序,操作和顶点着色器部分类似,不再赘述。
- 纹理相关操作(如果有纹理映射):
-
- 创建纹理对象,使用
gl.createTexture()函数,然后绑定纹理到特定的纹理单元,例如绑定到gl.TEXTURE0单元:
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE0, texture); - 创建纹理对象,使用
-
设置纹理参数,如纹理过滤方式(
gl.TEXTURE_MIN_FILTER和gl.TEXTURE_MAG_FILTER用于控制纹理缩小时和放大时的过滤模式,常见的有gl.NEAREST、gl.LINEAR等)、纹理环绕方式(gl.TEXTURE_WRAP_S和gl.TEXTURE_WRAP_T用于确定纹理坐标超出[0, 1]范围时的处理方式,常见的有gl.REPEAT、gl.CLAMP_TO_EDGE等),例如:gl.texParameteri(gl.TEXTURE0, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE0, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE0, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE0, gl.TEXTURE_WRAP_T, gl.REPEAT);

-
加载纹理图像数据,可以通过
gl.texImage2D()函数来加载外部的纹理图像(支持多种图像格式,如 PNG、JPEG 等),例如:const image = new Image();
image.onload = function() {
gl.texImage2D(gl.TEXTURE0, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
// 这里假设图像是RGBA格式,数据类型是无符号字节类型
};
image.src = ‘texture.png’; // 纹理图像的路径 -
在片段着色器中通过采样器(Sampler)来获取纹理颜色值,需要先在片段着色器中声明一个采样器变量(如
uniform sampler2D u_texture;),然后在 JavaScript 代码中获取其位置,并将纹理单元绑定到该采样器上,例如:const uTextureLocation = gl.getUniformLocation(program, ‘u_texture’);
gl.activeTexture(gl.TEXTURE0);
gl.uniform1i(uTextureLocation, 0); // 将纹理单元0绑定到采样器上
帧缓冲阶段
-
创建帧缓冲对象:
使用gl.createFramebuffer()函数创建帧缓冲对象,例如:const framebuffer = gl.createFramebuffer();
-
绑定帧缓冲并设置相关参数:
通过gl.bindFramebuffer()函数将创建好的帧缓冲绑定到特定的目标(如gl.FRAMEBUFFER),然后可以设置一些参数,比如绑定纹理或渲染缓冲(Renderbuffer)到帧缓冲的颜色附件、深度附件等位置,以确定渲染结果的存储方式。例如,绑定一个纹理作为颜色附件:gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
这里假设texture是之前创建并配置好的纹理对象,用于存储渲染后的颜色数据。
3. 渲染到帧缓冲并最终显示到屏幕:
在进行渲染操作(如前面的绘制图元操作)时,渲染结果就会存储到绑定的帧缓冲中对应的附件里,然后可以通过交换缓冲区(如果是双缓冲机制,常用于动画等场景,避免画面闪烁)等操作,最终将帧缓冲中的内容显示到屏幕上。在 WebGL 中,通常是浏览器自动处理将帧缓冲内容显示到对应的<canvas>元素所占据的屏幕区域,无需额外复杂的显式调用,但开发者可以通过控制帧缓冲的使用和渲染操作的时机等来间接影响显示效果。
以上就是 WebGL 图形渲染管线各阶段对应的主要 API 调用方式,整个过程较为复杂且各环节相互关联,需要不断实践和深入理解才能熟练掌握运用,实现想要的图形渲染效果。
相关文章:
webGL硬核知识:图形渲染管渲染流程,各个阶段对应的API调用方式
一、图形渲染管线基础流程概述 WebGL 的图形渲染管线大致可分为以下几个主要阶段,每个阶段都有其特定的任务,协同工作将 3D 场景中的物体最终转换为屏幕上呈现的 2D 图像: 顶点处理(Vertex Processing)阶段࿱…...
区块链详解
1. 概述 1.1 什么是区块链? 区块链是一种分布式数据库技术,它以链式数据结构的形式存储数据,每个数据块与前一个数据块相关联,形成了一个不断增长的数据链。每个数据块中包含了一定数量的交易信息或其他数据,这些数据…...
【EXCEL 逻辑函数】AND、OR、XOR、NOT、IF、IFS、IFERROR、IFNA、SWITCH
目录 AND:当所有条件都为真时返回 TRUE,否则返回 FALSE OR:当任一条件为真时返回 TRUE,否则返回 FALSE XOR:当奇数个条件为真时返回 TRUE,否则返回 FALSE NOT :反转逻辑值 IF:根…...
ubuntu下gdb调试ROS
参考: 使用VsCode进行ROS程序调试_ros vscode 调试-CSDN博客 https://blog.csdn.net/weixin_45031801/article/details/134399664?spm1001.2014.3001.5506 一、调试准备 1.1 CMakeLists改动 注释文件中的 set(CMAKE_BUILD_TYPE "Release") #构建类…...
Docke_常用命令详解
这篇文章分享一下笔者常用的Docker命令供各位读者参考。 为什么要用Docker? 简单来说:Docker通过提供轻量级、隔离且可移植的容器化环境,使得应用在不同平台上保持一致性、易于部署和管理,具体如下 环境一致性: Docker容器使得…...
使用vue2.0或vue3.0创建自定义组件
Vue2.0创建自定义组件 在 Vue 2.0 中创建自定义组件是一个相对简单的过程。以下是一个详细的步骤指南,帮助你创建一个自定义组件。 步骤 1: 创建 Vue 组件文件 首先,你需要创建一个新的 Vue 文件(.vue 文件)。假设我们要创建一…...
Elasticsearch-DSL高级查询操作
一、禁用元数据和过滤数据 1、禁用元数据_source GET product/_search {"_source": false, "query": {"match_all": {}} }查询结果不显示元数据 禁用之前: {"took" : 0,"timed_out" : false,"_shards" : {&quo…...
【Linux】重启系统后开不开机(内核模块丢失问题)
问题 重启后开不开机报错如下: FAILED failed to start load kernel moduiles 可以看到提示module dm_mod not found 缺少了dm_mod 在内核module目录中 reboot重启可以看到这个现象: 可以看到重启启动磁盘,加载不到root 原因 dm_mod模块…...
对golang的io型进程进行off-cpu分析
背景: 对于不能占满所有cpu核数的进程,进行on-cpu的分析是没有意义的,因为可能程序大部分时间都处在阻塞状态。 实验例子程序: 以centos8和golang1.23.3为例,测试下面的程序: pprof_netio.go package m…...
Springboot中使用Retrofit
Retrofit官网 https://square.github.io/retrofit/ 配置gradle implementation("com.squareup.okhttp3:okhttp:4.12.0")implementation ("com.squareup.retrofit2:retrofit:2.11.0")implementation ("com.squareup.retrofit2:converter-gson:2.11.0…...
Ubuntu中配置内网固定IP
文章目录 背景一、配置步骤(一)首先确认网卡名称(二)确认网关(三)备份配置文件(四)编辑配置文件(五)应用配置(六)验证配置 二、注意事…...
ExcelVBA编程输出ColorIndex与对应颜色色谱
标题 ExcelVBA编程输出ColorIndex与对应颜色色谱 正文 解决问题编程输出ColorIndex与对应色谱共56,打算分4纵列输出,标题是ColorIndex,Color,Name 1. 解释VBA中的ColorIndex属性 在VBA(Visual Basic for Applications)中ÿ…...
MySQL中in和exists的使用场景
在MySQL中,IN 和 EXISTS 是用于子查询的两种常见方法,它们在不同的场景下有不同的表现和适用性。下面我将详细介绍这两种方法的使用场景、优劣,并通过实验来说明问题。 IN 子查询 使用场景: 当子查询返回的结果集较小且不包含 …...
【多线程2】start 和 run 区别,终止线程,等待线程
Thread 类使用 start 方法,启动一个线程,对于同一个 Thread 对象来说,start 只能调用一次!!! 不怕名字起的长,就怕含义不清楚! 想要启动更多线程,就是得创建新的对象&am…...
富途证券C++面试题及参考答案
C++ 中堆和栈的区别 在 C++ 中,堆和栈是两种不同的内存区域,它们有许多区别。 从内存分配方式来看,栈是由编译器自动分配和释放的内存区域。当一个函数被调用时,函数内的局部变量、函数参数等会被压入栈中,这些变量的内存空间在函数执行结束后会自动被释放。例如,在下面的…...
Go使用sqlx操作MySQL完整指南
# Go使用sqlx操作MySQL完整指南## 1. 安装依赖bash go get github.com/go-sql-driver/mysql go get github.com/jmoiron/sqlx2. 数据库基础操作 package mainimport ("fmt"_ "github.com/go-sql-driver/mysql""github.com/jmoiron/sqlx" )// 定…...
Python 爬取网页文字并保存为 txt 文件教程
引言 在网络数据获取的过程中,我们常常需要从网页中提取有用的文字信息。Python 提供了强大的库来帮助我们实现这一目标。本教程将以https://theory.gmw.cn/2023 - 08/31/content_36801268.htm为例,介绍如何使用requests库和BeautifulSoup库爬取网页文字…...
时间序列预测论文阅读和相关代码库
时间序列预测论文阅读和相关代码库列表 MLP-based的时间序列预测资料DLinearUnetTSFPDMLPLightTS 代码库以及论文库:Time-Series-LibraryUnetTSFLightTS MLP-based的时间序列预测资料 我会定期把我的所有时间序列预测论文有关的资料链接全部同步到这个文章中&#…...
Mamba安装环境和使用,anaconda环境打包
什么是mamba Mamba是一个极速版本的conda,它是conda的C重新实现,使用多线程并行处理来加速包和依赖项的下载。 Mamba旨在提高安装、更新和卸载Python包的速度,同时保持与conda相同的兼容性和命令行接口。 Mamba的核心部分使用C实现ÿ…...
SSH连接成功,但VSCode连接不成功
环境 在实验室PC上连接服务器234 解决方案:在VSCode中重新添加远程主机 删除旧的VSCode Server 在远程主机上,VSCode会安装一个‘vscode-server’服务来支持远程开发,有时旧的‘vscode-server’文件可能会导致问题,删除旧的&am…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
