前言
本篇文章主要介绍纹理Texture、滤镜Filter、Mesh和Shader、渲染器相关的知识,这其中会涉及到的一些3D概念。PIXI底层使用WebGL/WebGPU来实现绘制,想要深入的理解PIXI必然要去学习3D知识,这是绕不开的,本文会尽可能的说明相关的3D概念从而加深对PIXI的理解。本文对应的PIXI版本是v8.1.0。
纹理Texture
纹理是什么?纹理通常被理解成图片,在计算机图形学中,通常情况下可以从不同格式的图片中来获取纹理数据。纹理可以为物体带来多彩的视觉效果,例如游戏中人物皮肤。
对于纹理需要知道几个关键知识点:
- 纹理数据:纹理数据通常都是图像数据(颜色值),但是可以是其他类型的数据例如法线、高度值等,在3D中实现相关的贴图效果
- 纹理映射:所谓的纹理映射UV Mapping是指纹理映射到模型的表面的操作,从而建立三维物体表面与纹理数据一一对应的关系
- 纹理坐标:在计算机图形中,3D世界上物体是有一系列顶点构成,而纹理可以映射到物体表面,就意味着纹理必然需要定义坐标数据,纹理坐标对应的坐标系被称为纹理坐标系,依据纹理坐标系定义的纹理坐标是2D坐标(需要说明的是:纹理坐标通常都是二维坐标,也有三维的),纹理映射就是将2D纹理坐标与3D顶点坐标一一对应从而为后面渲染提供相关的数据
在PIXI中纹理数据的获取可以使用Assets、Texture.from以及其他可渲染对象的from方法来获取,具体使用如下:
const texture1 = await Assets.load('https://pixijs.com/assets/bunny.png');
const texture2 = Texture.from('assets/image.png');
推荐使用Assets来进行纹理数据的获取,实际上在PIXI中纹理数据可以来源于图像、canvas、video,相关的获取都可以使用Assets来获取。
在PIXI中关于纹理的API中,本文主要关注的有两个:
- TextureSource:纹理数据来源类,存储一个纹理数据源对象
- Texture:纹理类,该类实例就代表着一个纹理对象
Texture的source属性指向的就是一个TextureSource对象,可见底层是依赖于TextureSource的。TextureSource类有些属性是需要重点关注的:
- magFilter:用于指定在纹理放大时所使用的过滤器
- minFilter:指定在纹理缩小时所使用的过滤器
- mipmapFilter:指定纹理使用 Mipmaps 时的过滤方式
- scaleMode:设置该属性会设置magFilter、minFilter、mipmapFilter
上面这些属性涉及到纹理采样的渲染中的一些问题,这里简要说明下渲染过程中纹理采样的处理:
纹理坐标称为uv,坐标范围都是[0.0, 1.0],在3D渲染时通常将uv坐标保存在顶点信息中,三角形内可以通过插值方式得到每个片元具体的uv坐标,通过uv坐标就可以从纹理中采样获取对应的颜色值,得到纹理颜色值就可以参入像素最终颜色计算,从而呈现在屏幕上
任何三维物体表面都可以展开成二维平面的,当纹理映射到物体表面必然会存在纹理无法完全覆盖物体表面的情况,即纹理过大或过小的问题。如何处理纹理过大或过小问题呢?
- 纹理分辨率过小,需要放大
- 纹理分辨率过大,需要缩小
纹理放大或缩小涉及到的背后的采样问题,相关处理非常复杂,这里只需要了解处理的基本思想就是从周围采样来额外生成相关信息。
上面提到的MipMap又是什么呢?这里简单了解下,毕竟非常复杂,Mipmaps 是一种优化技术,用于提高纹理映射的性能和质量。Mipmaps 是原始纹理的一系列缩小版本,当纹理被缩小时,可以使用相应尺寸的 Mipmap,从而减少失真并提高性能,使用 Mipmaps 可以解决纹理过大或过小的问题。
对于PIXI中由于都是二维模型,纹理坐标uv通常不需要开发者自己去具体设置,不过一旦涉及到高级应用可能就需要修改纹理坐标,从而实现相应的效果。
Mesh和shader
在PIXI有三个基础类:Geometry、Shader、Mesh,下面是一个三角形Mesh生成的逻辑,具体如下:
// 定义几何体
const geometry = new Geometry({
attributes: {
aPosition: [-100, -50, 100, -50, 0, 100]
},
});
// 定义shader
const shader = new Shader({
glProgram: new GlProgram({
vertex: `
in vec2 aPosition;
uniform mat3 uProjectionMatrix;
uniform mat3 uWorldTransformMatrix;
uniform mat3 uTransformMatrix;
void main() {
mat3 mvp = uProjectionMatrix * uWorldTransformMatrix * uTransformMatrix;
gl_Position = vec4((mvp * vec3(aPosition, 1.0)).xy, 0.0, 1.0);
}
`,
fragment: `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
}),
});
// 创建三角形Mesh
const mesh = new Mesh({ geometry, shader });
这里就根据上面的例子来具体说明Geometry、Shader、Mesh等API以及相关概念。
Mesh
在计算机图形学中,mesh表示是网格物体模型,网格物体模型描述三维物体的几何形状,它是有一系列顶点为基础从而形成线、面从而构成网格结构,是三维模型的最广泛使用的表示方法。
两个顶点之间连接成线,三个顶点组成一个三角面,三维模型就是由这样的大量的三角面构成。
在计算机图形学中,一个三维模型物体是由几何体和材质组成的:
- 几何体:通常使用Geometry表示,它代表着由顶点都成的几何形状
- 材质:通常使用Material表示,它表示着物体外观以及质感
在PIXI中使用Mesh API来创建一个物体模型:
- Mesh需要的几何体使用Geometry API来创建
- Mesh需要的材质使用Shader API来创建
const mesh = new Mesh({ geometry, shader });
Geometry
几何体是有一系列顶点组成的几何形状,在PIXI中使用Geometry API来创建一个几何体。开发者需要提供相关的顶点数据,包括顶点位置,面片索引、法相量、颜色值、UV 坐标等,位置数据必须提供。
const geometry = new Geometry({
attributes: {
aPosition: [-100, -50, 100, -50, 0, 100]
},
});
上面只提供了顶点位置属性数据,使用aPosition变量表示,实际上也可以是其他变量名,这边的顶点数据变量名是跟Shader中一一对应的。
这里需要说明的是:
- 顶点数据是要在后面的着色器程序中使用,就是说你的着色器程序需要什么数据就提供对应的数据,只有位置数据是必须的
- 在PIXI中顶点位置提供的是一个数组结构,在底层实际上是按照每2位表示一个顶点坐标的形式来获取,所以上面是三个点的坐标
Shader
在计算机图形学中,三维场景渲染出画面显示在屏幕上,这个处理过程被称为图形渲染管线,不同的图形规范的渲染管线过程可能存在不同,但是都存在顶点着色器、片段着色器的处理过程:
- 顶点着色器程序的功能是处理顶点数据,即每一个顶点都会经过该程序处理
- 片段着色器程序的功能是处理每一个片元(像素)进行颜色计算从而决定屏幕上像素的最终颜色
- 顶点数据是渲染管线的主要数据来源(每个顶点对象都有各种属性数据组成)
Shader是什么呢?Shader(着色器)是一种在计算机图形学中用于控制图像渲染过程的程序或代码段。着色器在GPU上执行,用于定义图形渲染管线中的各个阶段的行为。
所谓的材质Material本质上就是Shader编写的实现对应效果的程序,在PIXI中使用Shader API来定义材质:
const shader = new Shader({
glProgram: new GlProgram({
vertex: `
in vec2 aPosition;
uniform mat3 uProjectionMatrix;
uniform mat3 uWorldTransformMatrix;
uniform mat3 uTransformMatrix;
void main() {
mat3 mvp = uProjectionMatrix * uWorldTransformMatrix * uTransformMatrix;
gl_Position = vec4((mvp * vec3(aPosition, 1.0)).xy, 0.0, 1.0);
}
`,
fragment: `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
}),
});
在WebGL中一个顶点着色器和一个片段着色器链接在一起构成一个着色程序,所以在PIXI中按照这个逻辑实现:
- 使用GlProgram API创建一个着色器程序,也可以使用其他方式来创建,例如Shader.from
- vertex属性定义顶点着色器源代码
- fragment属性定义片段着色器源代码
着色器程序使用专门的编程语言来编写,这里以WebGL为例来说,WebGL是基于OpenGL ES规范的,而OpenGL ES着色器语言称为GLSL ES,所以具体的着色器程序具体编写需要去学习GLSL。
其他Mesh相关API
除了Mesh API外,还有其他便于创建对应模型的API,例如MeshPlane、MeshRope、MeshSimple,底层原理都是相通的,只是用于便捷地实现了不同的效果而已,这里不过多介绍,具体查看官网API。
这里需要特别说明的是:
在PIXI中,Mesh模型的顶点坐标是二维数据, 即PIXI中Mesh模型只能创建二维平面
滤镜Filter
在计算机图形学中,滤镜通常是指一种用于修改图像或图形外观的技术,可以应用于图像的像素以实现各种效果,例如模糊、锐化等。
在PIXI中滤镜的使用非常简单,可渲染对象都存在一个filters属性,应用滤镜只需要添加到该属性上即可
import { Sprite, BlurFilter, HardMixBlend } from 'pixi.js';
const sprite = Sprite.from('myTexture.png');
sprite.filters = new BlurFilter({ strength: 8 });
sprite.filters = [new BlurFilter({ strength: 8 }), new HardMixBlend()];
滤镜的技术实现基本上都是通过shader来实现的,PIXI提供的滤镜效果还是挺多的,具体滤镜效果查看官网。
开发者可以自己编写shader来实现各种炫酷的效果,这里推荐两个著名的shader网站,网站提供了大量的shader效果实现:
WebGL渲染器
在PIXI中想要单独创建渲染器可以使用下列API:
- WebGLRenderer:WebGL渲染器
- WebGPURenderer:WebGPU渲染器
- autoDetectRenderer:自动根据浏览器支持选择WebGL或者WebGPU,WebGPU优先
通常情况下应该很少直接创建渲染器,一旦涉及到渲染器部分那必然是复杂的,这里面涉及到大量的3D知识,这里也仅仅是对WebGL渲染器(WebGPU不熟就不介绍了)进行简要说明以及涉及到的一些关键点知识。
WebGL渲染器的基本使用:
const renderer = new WebGPURenderer();
await renderer.init();
document.body.appendChild(renderer.canvas);
const stage = new Container();
renderer.render(stage);
渲染器部分涉及到GPU缓冲区(颜色、深度、模板)、渲染目标(渲染内容到屏幕或者纹理)等,在PIXI中提供了大量的API实现来管理各个部分的逻辑。想要比较深入的理解这部分,需要结合源码以及具体的案例去看,在后续文章中会做相关分析梳理。
总结
本文介绍了纹理Texture、Mesh和Shader、滤镜Filter、WebGL渲染器以及涉及到的相关3D知识,可以结合官网相关细节从而加深对PIXI的理解和使用。
实际上还有很多点没有说明,例如纹理部分的RenderTetxure、Shader部分纹理的应用等。一些PIXI中关键知识还是需要进一步的使用和理解,后续也会结合具体的案例以及源码来进行进一步的说明。
截止到本文为止,PIXI入门的API学习以及介绍基本上就结束了,入门系列还会有一篇文章,主要侧重于大量案例的实践,目的有三个:
- 介绍并熟悉PIXI周围生态插件
- 大量案例实战从而熟练使用PIXI
- 为后续的源码分析做准备工作