webGL硬核知识:图形渲染管渲染流程,各个阶段对应的API调用方式

一、图形渲染管线基础流程概述

WebGL 的图形渲染管线大致可分为以下几个主要阶段,每个阶段都有其特定的任务,协同工作将 3D 场景中的物体最终转换为屏幕上呈现的 2D 图像:

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

二、各阶段对应的 WebGL API 调用方式
顶点处理阶段
  1. 创建缓冲区对象(Buffer Object)并绑定
    • 首先使用gl.createBuffer()函数创建一个缓冲区对象,这个缓冲区用于存储顶点数据(如顶点坐标、颜色等)。例如:

    const buffer = gl.createBuffer();

  • 然后通过gl.bindBuffer()函数将创建好的缓冲区绑定到特定的目标上,常见的目标有gl.ARRAY_BUFFER(用于存储顶点数组数据,比如顶点坐标、颜色等属性数组)和gl.ELEMENT_ARRAY_BUFFER(用于存储索引数据,在使用索引绘制时用到)。比如绑定用于存储顶点坐标数据的缓冲区:

    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

  1. 向缓冲区填充数据
    使用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);

  2. 创建并编译着色器(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);
  1. 获取属性位置并启用顶点属性数组
    在链接好的程序中,通过gl.getAttribLocation()函数获取顶点属性变量(如a_position)对应的位置索引,然后使用gl.enableVertexAttribArray()函数启用该顶点属性数组,以便后续传递数据给顶点着色器进行处理。例如:

    const aPositionLocation = gl.getAttribLocation(program, ‘a_position’);
    gl.enableVertexAttribArray(aPositionLocation);

  2. 指定顶点属性数据的读取方式
    使用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);

其中,xy是视口在屏幕中的起始坐标(通常以左下角为原点),widthheight分别是视口的宽度和高度。例如,设置整个画布区域为视口:

const canvas = document.getElementById('webglCanvas');
gl.viewport(0, 0, canvas.width, canvas.height);
片段处理阶段
  1. 片段着色器编写与相关操作(类似顶点着色器部分步骤)
    • 创建片段着色器对象,使用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是内置的输出变量,用于指定该片段最终的颜色值(这里设置为红色)。

  • 将编译好的片段着色器添加到之前创建的着色器程序中,并链接程序,操作和顶点着色器部分类似,不再赘述。
  1. 纹理相关操作(如果有纹理映射)
    • 创建纹理对象,使用gl.createTexture()函数,然后绑定纹理到特定的纹理单元,例如绑定到gl.TEXTURE0单元:

    const texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE0, texture);

  • 设置纹理参数,如纹理过滤方式(gl.TEXTURE_MIN_FILTERgl.TEXTURE_MAG_FILTER用于控制纹理缩小时和放大时的过滤模式,常见的有gl.NEARESTgl.LINEAR等)、纹理环绕方式(gl.TEXTURE_WRAP_Sgl.TEXTURE_WRAP_T用于确定纹理坐标超出[0, 1]范围时的处理方式,常见的有gl.REPEATgl.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绑定到采样器上

帧缓冲阶段
  1. 创建帧缓冲对象
    使用gl.createFramebuffer()函数创建帧缓冲对象,例如:

    const framebuffer = gl.createFramebuffer();

  2. 绑定帧缓冲并设置相关参数
    通过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 调用方式,整个过程较为复杂且各环节相互关联,需要不断实践和深入理解才能熟练掌握运用,实现想要的图形渲染效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/939612.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《深入浅出Apache Spark》系列⑤:Spark SQL的表达式优化

导读&#xff1a;随着数据量的快速增长&#xff0c;传统的数据处理方法难以满足对计算速度、资源利用率以及查询响应时间的要求。为了应对这些挑战&#xff0c;Spark SQL 引入了多种优化技术&#xff0c;以提高查询效率&#xff0c;降低计算开销。本文从表达式层面探讨了 Spark…

创建项目以及本地仓库和远程仓库并上传项目

创建项目以及本地仓库和远程仓库并上传项目 其详细流程如下&#xff1a; 1、本地创建项目 2、创建本地仓库&#xff08;若使用idea在创建项目时选择了创建.git本地仓库&#xff0c;则此步骤省略&#xff09; 进入到你需要上传的项目的目录下&#xff0c;右键找到Git Bah He…

快速解决oracle 11g中exp无法导出空表的问题

在一些生产系统中&#xff0c;有些时候我们为了进行oracle数据库部分数据的备份和迁移&#xff0c;会使用exp进行数据的导出。但在实际导出的时候&#xff0c;我们发现导出的时候&#xff0c;发现很多空表未进行导出。今天我们给出一个快速解决该问题的办法。 一、问题复现 我…

MySQL八股-MVCC入门

文章目录 当前读&#xff08;加锁&#xff09;快照读&#xff08;不加锁&#xff09;MVCC隐藏字段undo-log版本链A. 第一步B.第二步C. 第三步 readview MVCC原理分析RCA. 先来看第一次快照读具体的读取过程&#xff1a;B. 再来看第二次快照读具体的读取过程: RR隔离级别 当前读…

【已解决】启动此实时调试器时未使用必需的安全权限。要调试该进程,必须以管理员身份运行此实时调试器。是否调试该进程?

【已解决】启动此实时调试器时未使用必需的安全权限。要调试该进程&#xff0c;必须以管理员身份运行此实时调试器。是否调试该进程? 目录一、前言二、具体原因三、解决方法 目录 报错截图 一、前言 进行应用程序开发时&#xff0c;需要对w3wp进行附加调试等场景&#xff…

基于Qt的登陆界面设计

目标 自由发挥登录界面的应用场景&#xff0c;实现一个登录窗口的界面。 要求&#xff1a;每行代码都要有注释 代码 // 设置窗口大小为600x400像素 this->resize(600,400); // 设置窗口标题为"TheWitcher 巫师3&#xff1a;狂猎" this->setWindowTitle(&qu…

Elasticsearch 8.x 集成与 Java API 使用指南

目录 背景 版本区别 安装elaticsearch8.x服务 启动es服务 安装es管理平台 项目集成 pom.xml文件引入依赖 application.yml配置 ES初始化配置类实现 ES8.x常用API实现 1.判断es索引是否存在 2.删除索引 3.创建索引 4.新增文档 5.更新文档 6.根据id查询文档 7.根…

EfficientNet模型Pytorch版本具体实现

EfficientNet模型原理&#xff1a;EfficientNet&#xff1a;对模型深度、宽度和分辨率的混合缩放策略-CSDN博客 一、激活函数&#xff1a; EfficientNet模型使用了Swish激活函数而不是更常见的Relu激活函数 1、公式定义 Swish(x) x * sigmoid(x)是一个平滑的非线性激活函数…

Git连接远程仓库(超详细)

目录 一、Gitee 远程仓库连接 1. HTTPS 方式 2. SSH公钥方式 &#xff08;1&#xff09;账户公钥 &#xff08;2&#xff09;仓库公钥 仓库的 SSH Key 和账户 SSH Key 的区别&#xff1f;​ 二、GitHub远程仓库连接 1. HTTPS方式 2.SSH公钥方式 本文将介绍如何通过 H…

AutoMQ 流表一体新特性 Table Topic 发布: 无缝集成 AWS S3 Table 和 Iceberg

超越共享存储&#xff1a;使用 Apache Iceberg 中的 AutoMQ Table Topic 实现流处理与分析的统一 自 2023 年底官宣以来&#xff0c;AutoMQ 成功地将 Apache Kafka 从“Shared Nothing architecture”转变为“Shared Storage architecture”&#xff0c;这为京东、知乎、小红书…

Upload-labs 靶场(通关攻略)

WebShell 一句话木马: <?php eval($_POST[a])?> <?php system($_POST[a])?> 第一关&#xff08;删除前端js校验&#xff09; 删除return checkFile() 就能上传成功 第二关(抓包文件类型校验) BP抓包修改后缀 改为2.php后放行 第三关(上传php同种类型的不…

Linux —— 管理进程

一、查看进程 运行态&#xff08;Running&#xff09; 定义&#xff1a;处于运行态的进程正在 CPU 上执行指令。在单 CPU 系统中&#xff0c;同一时刻只有一个进程处于运行态&#xff1b;在多 CPU 或多核系统中&#xff0c;可能有多个进程同时处于运行态。示例&#xff1a; 当…

Linux脚本语言学习--下

4.Bash的变量 4.1.用户自定义变量 4.1.1.什么是变量 变量是计算机内存的单元&#xff0c;其中存放的值可以改变。当Shell脚本需要保存一些信息的时候&#xff0c;如一个文件名或是一个数字&#xff0c;就把他存放在一个变量中。每个变量有一个名字&#xff0c;所以很容易引用…

武汉市电子信息与通信工程职称公示了

2024年武汉市电子信息与通信工程专业职称公示了&#xff0c;本次公示通过人员有109人。 基本这已经是今年武汉市工程相关职称最后公示了&#xff0c;等待出证即可。 为什么有人好奇&#xff0c;一样的资料&#xff0c;都是业绩、论文等&#xff0c;有的人可以过&#xff0c;有的…

ModelScope-Agent(3):docker启动

目录 前两篇并没有使用到docker,但我看项目中是有docker配置的&#xff0c;不过没有在教程中写出来。学习了ragflow的docker配置流程之后&#xff0c;试一下modelscope-agent的配置。 先创建model-scope的cuda环境 conda create --name modelscope-agent python3.10 conda acti…

如何高效获取Twitter数据:Apify平台上的推特数据采集解决方案

引言 在数据分析和市场研究领域&#xff0c;Twitter&#xff08;现在的X&#xff09;数据一直是重要的信息来源。但是&#xff0c;自从Twitter更改API定价策略后&#xff0c;获取数据的成本大幅提升。本文将介绍一个经济实惠的替代方案。 为什么需要Twitter数据&#xff1f; …

MySql 中的解决某列中多个字段查询是否存在指定某个值, FIND_IN_SET 用法。

简言&#xff1a;今天公司数据库里面有个列是多个数据拼接而成的比如&#xff1a;**“,131113,749932833,749932825,749932826,749932827,749932828,749932829,”**想要通过sql 查找749932833值的列&#xff0c;很多同学第一想到的就是like 模糊匹配&#xff0c;模糊匹配不能保…

go引用包生成不了vendor的问题

比如我要引入github.com/jinzhu/gorm这个包. 1. 首先获取包 go get github.com/jinzhu/gorm 这时go.mod文件中也有这个包依赖信息了. 2. 然后构建vendor go mod vendor 结果发现vendor目录下没有生成对应的包, 而且modules.txt也注释掉这个包了. 原因是没有其进行引用, go…

基于 SSM 框架 Vue 电脑测评系统:引领电脑评测新方向

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

知识分享第三十天-力扣343.(整数拆分)

343 整数拆分 给定一个正整数 n&#xff0c;将其拆分为至少两个正整数的和&#xff0c;并使这些整数的乘积最大化。 返回你可以获得的最大乘积。 示例 1: 输入: 2 输出: 1 解释: 2 1 1, 1 1 1。 示例 2: 输入: 10 输出: 36 解释: 10 3 3 4, 3 3 4 36。 说明: 你可…