当前位置:首页 > 科技  > 软件

PixiJS 源码解读:绘制矩形的渲染过程讲解

来源: 责编: 时间:2023-10-10 18:32:13 220观看
导读大家好,我是前端西瓜哥。之前写了一篇 PixiJS 绘制矩形,简单说了一下 PixiJS 是怎么绘制矩形的。《PixiJS 源码解读:绘制矩形,底层都做了什么?》它更多的讲解上层的东西,没花太多笔墨描绘底层渲染的流程。所以我写了这篇文

2ZB28资讯网——每日最新资讯28at.com

大家好,我是前端西瓜哥。2ZB28资讯网——每日最新资讯28at.com

之前写了一篇 PixiJS 绘制矩形,简单说了一下 PixiJS 是怎么绘制矩形的。2ZB28资讯网——每日最新资讯28at.com

《PixiJS 源码解读:绘制矩形,底层都做了什么?》2ZB28资讯网——每日最新资讯28at.com

它更多的讲解上层的东西,没花太多笔墨描绘底层渲染的流程。所以我写了这篇文章,对渲染流程进行补充讲解。2ZB28资讯网——每日最新资讯28at.com

PixiJS 版本为 7.2.4。2ZB28资讯网——每日最新资讯28at.com

要求读者熟悉 WebGL 的基础知识。2ZB28资讯网——每日最新资讯28at.com

本文会 以绘制设置了填充和描边的矩形为例子,看底层 WebGL 的调用执行。2ZB28资讯网——每日最新资讯28at.com

业务层代码:2ZB28资讯网——每日最新资讯28at.com

const app = new PIXI.Application({  width: 500,  height: 300,  background: "#cc0", //(土黄色)});document.body.appendChild(app.view);const graph = new PIXI.Graphics();graph.beginFill(0xff0044); // 红色填充色graph.lineStyle({ color: "blue", width: 4 }); // 蓝色描边graph.drawRect(90, 70, 300, 100);app.stage.addChild(graph);

绘制结果为:2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

创建 gl

第一步是创建 gl 对象,上下文类型优先使用 "webgl2"。2ZB28资讯网——每日最新资讯28at.com

如果不支持,会降级为 "webgl"、"experimental-webgl"。2ZB28资讯网——每日最新资讯28at.com

gl = canvas.getContext("webgl2", options);

gl 在 renderer 渲染器初始化的时候构建的,可通过 app.renderer.gl 拿到。2ZB28资讯网——每日最新资讯28at.com

构建着色器代码片段

定义 顶点着色器 和 片元着色器。2ZB28资讯网——每日最新资讯28at.com

着色器(Shader)是一种类 C 语言 GLSL,用于描述需要绘制的 顶点信息和颜色信息。2ZB28资讯网——每日最新资讯28at.com

着色器模板

首先是 字符串模板,等着根据配置填充成一个完整的着色器代码片段。2ZB28资讯网——每日最新资讯28at.com

顶点着色器的模板(后面会基于它生成真正可用的着色器)位于 packages/core/src/batch/texture.vert 中。2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

batch 文件夹都是和 批量绘制 有关的逻辑,批量、减少 draw call 正是 PixiJS 高效绘制的秘诀。2ZB28资讯网——每日最新资讯28at.com

precision highp float;attribute vec2 aVertexPosition;attribute vec2 aTextureCoord;attribute vec4 aColor;attribute float aTextureId;uniform mat3 projectionMatrix;uniform mat3 translationMatrix;uniform vec4 tint;varying vec2 vTextureCoord;varying vec4 vColor;varying float vTextureId;void main(void){    gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);    vTextureCoord = aTextureCoord;    vTextureId = aTextureId;    vColor = aColor * tint;}

片元着色器和颜色有关。2ZB28资讯网——每日最新资讯28at.com

varying vec2 vTextureCoord;varying vec4 vColor;varying float vTextureId;uniform sampler2D uSamplers[%count%];void main(void){    vec4 color;    %forloop%    gl_FragColor = color * vColor;}

这里的 %count% 和%forloop% 是占位符,会在之后进行替换。2ZB28资讯网——每日最新资讯28at.com

最终着色器代码片段

在 renderer 初始化时,上面的模板会进行一系列的改造,两个着色器最终转换为下面的样子。2ZB28资讯网——每日最新资讯28at.com

顶点着色器(Vertex Shader)和顶点的位置、大小有关。2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

补充一些简单注释说明。2ZB28资讯网——每日最新资讯28at.com

顶点着色器

precision highp float; // 浮点数使用高精度#define SHADER_NAME pixi-shader-2precision highp float;attribute vec2 aVertexPosition; // 顶点位置 x 和 yattribute vec2 aTextureCoord; // 纹理坐标,会传给片元着色器attribute vec4 aColor; // 颜色,rgba,会传给片元着色器attribute float aTextureId; // 纹理单元 ID,会传给片元着色器uniform mat3 projectionMatrix; // 投影矩阵uniform mat3 translationMatrix; // 平移变换矩阵uniform vec4 tint; // 改变颜色,实现滤镜效果,会和 aColor 相乘传给片元着色器varying vec2 vTextureCoord; // varing 都是用来传递的varying vec4 vColor;varying float vTextureId;void main(void){    //  进行一系列矩阵乘法运算,将最后的点传给内置的着色器变量,设置点的位置    gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);   // 下面都是要传给片元着色器的变量    vTextureCoord = aTextureCoord;    vTextureId = aTextureId;    vColor = aColor * tint;}

片元着色器

片元着色器(Fragment Shader)用于描述顶点围成区域的像素颜色。2ZB28资讯网——每日最新资讯28at.com

下面是片元着色器的最终代码,同样我会加一些注释说明2ZB28资讯网——每日最新资讯28at.com

precision mediump float;#define SHADER_NAME pixi-shader-2varying vec2 vTextureCoord; // 纹理坐标,varying vec4 vColor; // 颜色varying float vTextureId; // 使用哪一个纹理采样器uniform sampler2D uSamplers[16]; // 16 个纹理采样器void main(void){  vec4 color;   if(vTextureId < 0.5) {    // 从纹理采样器(比如图片转换过来的像素点集合)中,提取特定位置的像素点    color = texture2D(uSamplers[0], vTextureCoord);  }else if(vTextureId < 1.5) {    color = texture2D(uSamplers[1], vTextureCoord);  }  // ...  } else {    color = texture2D(uSamplers[15], vTextureCoord);  }    // 叠加颜色值,和纹理采样器取得的颜色值,赋值给片元着色器内置变量  gl_FragColor = color * vColor;}

如果没有设置纹理,PixiJS 会给一个默认的兜底用纹理对象,一个 16x16 的白色方形。2ZB28资讯网——每日最新资讯28at.com

这两个着色器片段会保存到 Shader 实例中,放到 app.render.shader 下。2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

编译着色器程序

第一次调用 renderer 渲染器 render 方法时,PixiJS 会 创建顶点着色器对象和片元着色器对象。2ZB28资讯网——每日最新资讯28at.com

这些逻辑是在 generateProgram 方法中实现的。该方法的核心代码:2ZB28资讯网——每日最新资讯28at.com

function generateProgram(gl, program) {  //(1)创建顶点着色器对象、片元着色器对象等  const glVertShader = compileShader(gl, gl.VERTEX_SHADER, program.vertexSrc);  const glFragShader = compileShader(    gl,    gl.FRAGMENT_SHADER,    program.fragmentSrc  );  // 创建程序对象  const webGLProgram = gl.createProgram();  //(2)绑定 attribute  // keys 为 ['aColor', 'aTextureCoord', 'aTextureId', 'aVertexPosition']  for (let i = 0; i < keys.length; i++) {    program.attributeData[keys[i]].location = i;    // 将属性绑定到顶点着色器的制定位置    // 如:gl.bindAttribLocation(gl.program, 0, "aColor");    gl.bindAttribLocation(webGLProgram, i, keys[i]);  }  // 删除着色器对象,释放内存  gl.deleteShader(glVertShader);  gl.deleteShader(glFragShader);  //(3)绑定 uniformLocation(准确来说是拿地址,还没正式绑定)  // 属性(对应 i 变量)有:projectionMatrix、tint、translationMatrix、uSamplers  for (const i in program.uniformData) {    const data = program.uniformData[i];    uniformData[i] = {      location: gl.getUniformLocation(webGLProgram, i),      value: defaultValue(data.type, data.size),    };  }  const glProgram = new GLProgram(webGLProgram, uniformData);  return glProgram;}

分成三个主要步骤。2ZB28资讯网——每日最新资讯28at.com

创建着色器对象、程序对象。2ZB28资讯网——每日最新资讯28at.com

compileShader 实现:2ZB28资讯网——每日最新资讯28at.com

function compileShader(gl, type, src) {  const shader = gl.createShader(type);  gl.shaderSource(shader, src);  gl.compileShader(shader);    gl.attachShader(webGLProgram, glVertShader);  gl.attachShader(webGLProgram, glFragShader);  // ...  gl.linkProgram(webGLProgram);  return shader;}

绑定 attribute 类型的变量 (但此时还没传入 Buffer 数据,只是设置了如何访问等操作);2ZB28资讯网——每日最新资讯28at.com

绑定 uniform 类型的变量。2ZB28资讯网——每日最新资讯28at.com

之后在 app.renderer.shader.bind 方法内执行下面代码,应用刚刚创建的程序对象。2ZB28资讯网——每日最新资讯28at.com

this.gl.useProgram(glProgram.program);

渲染阶段

前面做的是准备工作,编译着色器。2ZB28资讯网——每日最新资讯28at.com

接下来就是渲染阶段。2ZB28资讯网——每日最新资讯28at.com

PIXI.Ticker 定时器会在渲染下一帧前调用 renderer.render 方法,进入 WebGL 的渲染流程。2ZB28资讯网——每日最新资讯28at.com

清空画布填充背景色

首先是清空画布。2ZB28资讯网——每日最新资讯28at.com

// 入口方法:renderer.renderTexture.clearclass ObjectRendererSystem {  render(displayObject, options) {    // ...    // (1) 清空画布,并指定颜色    renderer.renderTexture.clear();    // ...  }}

它会执行 clear 方法2ZB28资讯网——每日最新资讯28at.com

class FramebufferSystem {  clear(r, g, b, a, mask = BUFFER_BITS.COLOR | BUFFER_BITS.DEPTH) {    const { gl } = this;    // 背景色 #cc0 转换为 rbga 格式:    // (0.800000011920929, 0.800000011920929, 0, 1)    gl.clearColor(r, g, b, a);    // 清空颜色和深度缓存    gl.clear(mask);  }}

递归调用 render

递归图形树(app.stage),调用它们(继承了 IRenderableObject 接口类型)的 render 方法,它们会拿到 renderer 对象,然后执行自己的渲染逻辑。2ZB28资讯网——每日最新资讯28at.com

// app.stage 是 Container 实例class Container extends DisplayObject {  render(renderer) {    // ...    this._render(renderer); // 真正的渲染逻辑    for (let i = 0, j = this.children.length; i < j; ++i) {      this.children[i].render(renderer);    }  }}

对于前文的示例代码,会分析矩形属性,构建顶点和片元数据,然后执行 WebGL 的绘制 API。2ZB28资讯网——每日最新资讯28at.com

对矩形三角化,构建顶点和片元数据

先基于 x、y、width、height 计算出矩形的 4 个顶点放到 points。2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

然后进行三角化。三角化就是将图形转换为对应的三角形的组合。2ZB28资讯网——每日最新资讯28at.com

所谓图形的渲染,其实就是绘制一个个小的三角形,组成特定的形状。这些三角形的点,根据不同图形(比如矩形和圆形),需要用不同算法去计算出来,然后把数据通过 WebGL 命令交给 GPU,让它帮我们绘制出来。2ZB28资讯网——每日最新资讯28at.com

首先是填充的三角化(对应  buildRectangle.triangulate() )。2ZB28资讯网——每日最新资讯28at.com

基于前面的 4 个点得到填充块的 4 个点,并设置对应的索引值 indices,之后调用 gl.drawElements() 需要用到。2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

接着是描边的三角化(对应 buildLine())。2ZB28资讯网——每日最新资讯28at.com

下面是绘制描边的代码片段:2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

PixiJS 的计算逻辑很复杂,这是因为涉及到连接方式、末端样式的情况。2ZB28资讯网——每日最新资讯28at.com

同样,也要计算它的顶点、索引、纹理坐标。2ZB28资讯网——每日最新资讯28at.com

西瓜哥我将最终的填充和描边产生的点,做了一下可视化。2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

用的是 desmos 可视化工具,这里给一下这个可视化链接:2ZB28资讯网——每日最新资讯28at.com

https://www.desmos.com/calculator/r3dwqeweu2?lang=zh-CN。2ZB28资讯网——每日最新资讯28at.com

最后计算好的三角化数据会保存到 graph 对象的 batches 数组下(batches 表示要批量处理的意思)。2ZB28资讯网——每日最新资讯28at.com

batch 对象包括顶点坐标(vertexData)、颜色(_batchRGB)、索引(indices)和纹理坐标(uvs)。2ZB28资讯网——每日最新资讯28at.com

下面是填充色对应的数据:2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

批量渲染

这里产生了两个 batch 对象(对应填充和描边),然后遍历传给 BatchRender 类的 render 方法。说是 render 方法,其实并不立即 render,而是将 batch 对象的数据解读和保存起来,之后 flush 时才正式将数据加到 WebGL 里。2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

这些属性会组合拼装在一个类型数组里。6 个一组,逐顶点绘制。2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

传完后,会调用 BatchRender 类的 flush 方法,将顶点数据和索引数组通过 gl.bufferData() 进行绑定。2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

绑定 uniform 值

在 ShaderSystem 类的 syncUniforms 中,会依次设置好各个 uniform 变量:tint、translationMatrix、uSamplers、projectionMatrix。2ZB28资讯网——每日最新资讯28at.com

class ShaderSystem {    syncUniforms(group, glProgram, syncData) {    // 生成同步 uniform 的函数(不同 uniform 的函数不同)    const syncFunc =         group.syncUniforms[this.shader.program.id] ||         this.createSyncGroups(group);    // 同步!    syncFunc(glProgram.uniformData, group.uniforms, this.renderer, syncData);  }  createSyncGroups(group) {    const id = this.getSignature(group, this.shader.program.uniformData, "u");    if (!this.cache[id]) {      this.cache[id] = generateUniformsSync(group, this.shader.program.uniformData);    }    group.syncUniforms[this.shader.program.id] = this.cache[id];    return group.syncUniforms[this.shader.program.id];  }  }

下面是设置 tint 的方法:2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

绑定纹理

绑定纹理。2ZB28资讯网——每日最新资讯28at.com

class TextureSystem {  bind(texture, location = 0) {    const { gl } = this;    // 开启    gl.activeTexture(gl.TEXTURE0 + location);    // ...    gl.bindTexture(texture.target, glTexture.texture);    // ...  }}

因为示例并不绘制图片,PixiJS 会提供默认的的白色纹理对象(所有值都是 1),这样颜色值和其相乘,结果还是原来的颜色值。2ZB28资讯网——每日最新资讯28at.com

渲染

最后调用 drawBatches 进行绘制。2ZB28资讯网——每日最新资讯28at.com

drawBatches() {  const dcCount = this._dcIndex;  const { gl, state: stateSystem } = this.renderer;  const drawCalls = _BatchRenderer._drawCallPool;  let curTexArray = null;  for (let i = 0; i < dcCount; i++) {    const { texArray, type, size, start, blend } = drawCalls[i];    if (curTexArray !== texArray) {      curTexArray = texArray;      // 刚刚提到的纹理绑定逻辑      this.bindAndClearTexArray(texArray);    }    this.state.blendMode = blend;    stateSystem.set(this.state);    // 绘制 API    gl.drawElements(type, size, gl.UNSIGNED_SHORT, start * 2);  }}

最后我们就绘制出一个有填充和描边的矩形了。2ZB28资讯网——每日最新资讯28at.com

2ZB28资讯网——每日最新资讯28at.com

之后 Ticker 会不断地在绘制下一帧时调用 renderer 的 render 方法进行渲染,如果图形没改变(比如通过 dirtyId 和 cacheDirty 是否相同判断),我们会跳过三角化的环节,使用缓存好的数据去绘制渲染。2ZB28资讯网——每日最新资讯28at.com

结尾

PixiJS 绘制图形使用了 WebGL,为了利用 GPU 的并行能力,需要给着色器一次性提供尽可能多的顶点和颜色信息。2ZB28资讯网——每日最新资讯28at.com

PixiJS 提供了一些基础图形,比如矩形。绘制时会根据图形属性信息进行三角化,最后将所有的信息组合起来,一次性提供给 WebGL。2ZB28资讯网——每日最新资讯28at.com

这篇文章其实断断续续写了好久,PixiJS 里的弯弯道道挺多的,经常调试了半天就是找不着北了,一度搁置。最后还是硬着头皮不断地调试和思考,总算把这篇文章结束掉了。2ZB28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-12747-0.htmlPixiJS 源码解读:绘制矩形的渲染过程讲解

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Java 集合框架超详细!

下一篇: Springboot整合Hutool自定义注解实现数据脱敏

标签:
  • 热门焦点
  • K60 Pro官方停产 第三方瞬间涨价

    K60 Pro官方停产 第三方瞬间涨价

    虽然没有官方宣布,但Redmi的一些高管也已经透露了,Redmi K60 Pro已经停产且不会补货,这一切都是为了即将到来的K60 Ultra铺路,属于厂家的正常操作。但有意思的是该机在停产之后
  • 影音体验是真的强 简单聊聊iQOO Pad

    影音体验是真的强 简单聊聊iQOO Pad

    大公司的好处就是产品线丰富,非常细分化的东西也能给你做出来,例如早先我们看到了新的vivo Pad2,之后我们又在iQOO Neo8 Pro的发布会上看到了iQOO的首款平板产品iQOO Pad。虽
  • 中兴AX5400Pro+上手体验:再升级 双2.5G网口+USB 3.0这次全都有

    中兴AX5400Pro+上手体验:再升级 双2.5G网口+USB 3.0这次全都有

    2021年11月的时候,中兴先后发布了两款路由器产品,中兴AX5400和中兴AX5400 Pro,从产品命名上就不难看出这是隶属于同一系列的,但在外观设计上这两款产品可以说是完全没一点关系
  • 5月安卓手机好评榜:魅族20 Pro夺冠

    5月安卓手机好评榜:魅族20 Pro夺冠

    性能榜和性价比榜之后,我们来看最后的安卓手机好评榜,数据来源安兔兔评测,收集时间2023年5月1日至5月31日,仅限国内市场。第一名:魅族20 Pro好评率:97.50%不得不感慨魅族老品牌还
  • 在线图片编辑器,支持PSD解析、AI抠图等

    在线图片编辑器,支持PSD解析、AI抠图等

    自从我上次分享一个人开发仿造稿定设计的图片编辑器到现在,不知不觉已过去一年时间了,期间我经历了裁员失业、面试找工作碰壁,寒冬下一直没有很好地履行计划.....这些就放在日
  • JVM优化:实战OutOfMemoryError异常

    JVM优化:实战OutOfMemoryError异常

    一、Java堆溢出堆内存中主要存放对象、数组等,只要不断地创建这些对象,并且保证 GC Roots 到对象之间有可达路径来避免垃 圾收集回收机制清除这些对象,当这些对象所占空间超过
  • 2天涨粉255万,又一赛道在抖音爆火

    2天涨粉255万,又一赛道在抖音爆火

    来源:运营研究社作者 | 张知白编辑 | 杨佩汶设计 | 晏谈梦洁这个暑期,旅游赛道彻底火了:有的「地方」火了&mdash;&mdash;贵州村超旅游收入 1 个月超过 12 亿;有的「博主」火了&m
  • 华为HarmonyOS 4升级计划公布:首批34款机型今日开启公测

    华为HarmonyOS 4升级计划公布:首批34款机型今日开启公测

    8月4日消息,今天下午华为正式发布了HarmonyOS 4系统,在更流畅的前提下,还带来了不少新功能,UI设计也有变化,会让手机焕然一新。华为宣布,首批机型将会在
  • iQOO 11S屏幕细节公布:首发三星2K E6全感屏 安卓最好的直屏手机

    iQOO 11S屏幕细节公布:首发三星2K E6全感屏 安卓最好的直屏手机

    日前iQOO手机官方宣布,新一代电竞旗舰iQOO 11S将会在7月4日19:00正式与大家见面。随着发布时间的日益临近,官方关于该机的预热也更加密集,截至目前已
Top