news 2026/9/19 4:44:41

PixiJS 过滤器(Filters)完全指南:内置滤镜、高级混合模式与自定义着色器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PixiJS 过滤器(Filters)完全指南:内置滤镜、高级混合模式与自定义着色器

PixiJS 过滤器(Filters)完全指南:内置滤镜、高级混合模式与自定义着色器

【免费下载链接】pixijsThe HTML5 Creation Engine: Create beautiful digital content with the fastest, most flexible 2D WebGL renderer.项目地址: https://gitcode.com/gh_mirrors/pi/pixijs

过滤器(Filters)是 PixiJS 后处理体系的核心:它们能对任意显示对象及其子树施加模糊、颜色调整、噪点、置换扭曲乃至完全自定义的着色器效果。本文以 filters.md 为骨架,结合 src/filters 目录下的真实源码与 examples 中的示例,系统讲解五种内置过滤器、21 种高级混合模式、两种自定义过滤器写法以及渲染管线的底层原理。读完本文,你将能够在自己的 PixiJS 应用中直接套用所有示例代码,并写出可同时运行于 WebGL 与 WebGPU 的 GLSL/WGSL 过滤器。

过滤器是什么:对显示对象施加后处理

在 PixiJS 中,过滤器(Filter)是对显示对象渲染结果的一次"后处理":先把对象渲染到离屏纹理,再用过滤器的着色器程序把该纹理处理一遍后画回主帧缓冲。任何继承自Container的对象(Sprite、Graphics、Text 等)都可以通过filters属性挂载过滤器。

import { Assets, Sprite, BlurFilter, NoiseFilter } from 'pixi.js'; const texture = await Assets.load('photo.png'); const sprite = new Sprite(texture); // 单个过滤器 sprite.filters = new BlurFilter({ strength: 8 }); // 多个过滤器(按顺序依次应用) sprite.filters = [ new BlurFilter({ strength: 8 }), new NoiseFilter({ noise: 0.5 }), ];

[!NOTE]过滤器顺序很重要。它们按数组顺序依次执行,每个过滤器处理的是上一个过滤器的输出结果,因此顺序不同,最终效果也不同。

在源码层面,Filter 类 继承自Shader,并维护一份Filter.defaultOptions作为所有过滤器的默认配置(blendMode: 'normal'resolution: 1padding: 0antialias: 'off'blendRequired: falseclipToViewport: true)。构造过滤器时传入的选项会与默认值合并。

内置过滤器一览

PixiJS 内置了五个开箱即用的过滤器,全部位于 src/filters/defaults 目录:

过滤器用途
AlphaFilter施加统一的透明度
BlurFilter高斯模糊
ColorMatrixFilter通过 5x4 矩阵做颜色变换
DisplacementFilter使用置换贴图纹理扭曲图像
NoiseFilter添加随机噪点,营造颗粒质感

AlphaFilter(透明度)

import { AlphaFilter } from 'pixi.js'; sprite.filters = new AlphaFilter({ alpha: 0.5 });

alpha取值范围 0(完全透明)到 1(完全不透明)。从源码看,AlphaFilter 的defaultOptions.alpha1,滤镜运行时会把它写入名为uAlpha的 uniform,并且暴露了可读写的alpha属性供运行时动态修改。

值得注意的使用建议写在源码注释中:当需要对整个显示对象树施加统一透明度时,优先使用AlphaFilter而非Container.alpha。因为Container.alpha是逐元素(逐层)相乘的,多个半透明子元素叠加时会出现视觉上的重叠加深;而AlphaFilter是在整棵树渲染完成后统一施加 alpha,表现更符合直觉。同时它还能免费获得所有过滤器共有的能力——例如为滤镜指定blendMode让整棵树与背景混合、或在容器上设置filterArea做裁剪。

BlurFilter(高斯模糊)

import { BlurFilter } from 'pixi.js'; sprite.filters = new BlurFilter({ strength: 8, // 模糊强度(默认 8) quality: 4, // 模糊趟数(默认 4) kernelSize: 5, // 内核尺寸(默认 5) });

也可以只对单轴模糊:用strengthX/strengthY分别控制水平与垂直方向的强度,适合制作运动模糊或方向性光晕。

从实现看,BlurFilter 内部并不直接做二维模糊,而是组合了两个单方向的 BlurFilterPass(水平一趟 + 垂直一趟),利用高斯模糊的可分离性把 O(n²) 的卷积拆成两次 O(n) 卷积,大幅降低开销。apply方法中两个方向都非零时会从TexturePool申请一张同尺寸临时纹理:先水平模糊到临时纹理,再垂直模糊到输出,最后归还纹理。关于各参数的源码细节:

  • strength同时设置 X/Y 强度;读取时若strengthX !== strengthY会抛出异常(见 BlurFilter.ts#L289-L297)。旧版 API 的blur/blurX/blurY属性自 8.3.0 起已废弃,请改用strength系列。
  • kernelSize决定卷积核精度,可选值限定为5、7、9、11、13、15(奇数),数值越大精度越高、开销也越大(见 BlurFilter.ts#L76-L79)。
  • quality对应模糊趟数,越高越平滑但越慢。
  • repeatEdgePixels置为true时会对边缘像素做 clamp(钳制采样),避免模糊把透明边缘"卷"进来;此时padding自动归零(见 BlurFilter.ts#L261-L271)。
  • legacy选项(默认false)用于恢复 v8 之前的旧模糊趟行为(强度按趟数均匀分摊而非优化的折半方案),并会禁用 WebGPU 的按趟 UBO 批处理,一般无需开启。

仓库示例 examples/filters_blur.ts 展示了真实用法:创建两个BlurFilter实例分别挂到两个精灵上,然后在app.ticker中用Math.cos/Math.sin驱动模糊强度做呼吸式动画:

const blurFilter1 = new BlurFilter(); const blurFilter2 = new BlurFilter(); littleDudes.filters = [blurFilter1]; littleRobot.filters = [blurFilter2]; app.ticker.add(() => { count += 0.005; blurFilter1.blur = 20 * Math.cos(count); // 注意:8.3 起建议改为 strength blurFilter2.blur = 20 * Math.sin(count); });

ColorMatrixFilter(颜色变换)

import { ColorMatrixFilter } from 'pixi.js'; const colorMatrix = new ColorMatrixFilter(); colorMatrix.brightness(0.5, false); colorMatrix.contrast(0.8, false); colorMatrix.saturate(1.2, true); // true = 与当前矩阵相乘(累积效果) sprite.filters = colorMatrix;

ColorMatrixFilter用一张5x4 矩阵(20 个元素)对每个像素的 RGBA 做线性变换,类型定义见 ColorMatrixFilter.ts#L19。所有便捷方法的第二个参数multiply控制合并方式:false表示用新矩阵替换当前矩阵;true表示新矩阵与当前矩阵相乘,从而把多次颜色调整累积起来(内部由_multiply实现 4 行 x 5 列的手写矩阵乘法,见 ColorMatrixFilter.ts#L141-L172)。

可用预设方法(均带multiply参数):

brightnesscontrastsaturatedesaturategreyscale(别名grayscale)、blackAndWhitehuenegativesepiatechnicolorpolaroidtoBGRkodachromebrownivintagecolorTonenightpredatorlsdreset

此外还有tint(color, multiply)方法,接受任意 ColorSource(如0xff0000'green'),适合做整体染色。所有颜色调整都可以在运行时连续调用、动态切换,例如实现昼夜过渡(白天 → 夜晚滤镜)。

DisplacementFilter(置换贴图)

import { Sprite, DisplacementFilter } from 'pixi.js'; const displacementSprite = Sprite.from('displacement-map.png'); sprite.filters = new DisplacementFilter({ sprite: displacementSprite, scale: 20, });

DisplacementFilter用一张 Sprite 的纹理作为置换贴图:贴图中每个像素的红色通道决定水平位移量,绿色通道决定垂直位移量scale既可以是单个数字(均匀缩放),也可以传入{ x, y }形式的PointData分别控制两个方向(见 DisplacementFilter.ts#L50-L63),默认值20

源码中有几个值得注意的实现细节(DisplacementFilter.ts):

  • 构造时传入的sprite会被自动设为renderable = false(L174-L176),即贴图精灵本身不会被渲染出来,只作为数据源,因此你完全可以把它加到舞台上并移动/旋转它来驱动动画效果;
  • 每次apply时通过filterManager.calculateSpriteMatrix计算贴图矩阵,并从sprite.worldTransform提取旋转分量写入uRotation(L186-L216),保证贴图跟随精灵的变换;
  • 缩放值通过filter.scale.x/filter.scale.y可随时读写。

典型的应用场景包括水波涟漪、热浪扭曲、动态过渡转场。仓库示例 examples/filters_displacement.ts 与 examples/filters_displacement_interactive.ts 可作参考(对应贴图资源位于 examples/assets/pixi-filters 和 examples/assets/pond)。

NoiseFilter(噪点)

import { NoiseFilter } from 'pixi.js'; sprite.filters = new NoiseFilter({ noise: 0.5, // 噪点强度(默认 0.5) seed: Math.random(), // 随机种子(默认 Math.random()) });

noise控制噪点强度(0.1 细微颗粒、0.5 中等、1.0 最强);seed决定噪点图案——相同 seed 产生完全一致的噪点图案,适合做可复现的胶片颗粒或静态雪花效果。两者在运行时均可通过filter.noisefilter.seed属性动态调整(见 NoiseFilter.ts#L165-L202)。

高级混合模式

除了五种过滤器,PixiJS 还通过独立导入提供高级混合模式。与过滤器不同,它们不是直接应用的后处理滤镜,而是注册到容器上的混合模式(blendMode),由渲染管线在混合阶段生效:

import 'pixi.js/advanced-blend-modes'; sprite.blendMode = 'color-burn';

导入后即可使用以下 21 种混合模式(字符串 → 实现类):

混合模式字符串
'color'ColorBlend
'color-burn'ColorBurnBlend
'color-dodge'ColorDodgeBlend
'darken'DarkenBlend
'difference'DifferenceBlend
'divide'DivideBlend
'exclusion'ExclusionBlend
'hard-light'HardLightBlend
'hard-mix'HardMixBlend
'lighten'LightenBlend
'linear-burn'LinearBurnBlend
'linear-dodge'LinearDodgeBlend
'linear-light'LinearLightBlend
'luminosity'LuminosityBlend
'negation'NegationBlend
'overlay'OverlayBlend
'pin-light'PinLightBlend
'saturation'SaturationBlend
'soft-light'SoftLightBlend
'subtract'SubtractBlend
'vivid-light'VividLightBlend

从源码看,导入pixi.js/advanced-blend-modes实际执行的是 init.ts:把上述 21 个混合类通过extensions.add(...)注册进扩展系统,随后渲染器就能识别对应的混合模式字符串。

高 DPI 渲染器上的分辨率问题

高级混合模式需要采样当前渲染目标来把源像素与目标像素(背景)做混合。与其他过滤器一样,它们创建时使用Filter.defaultOptions,其默认resolution1(为了性能)。

如果你的渲染器使用了非1的分辨率(例如高 DPI 屏),且高级混合模式出现被裁剪、被缩放、或只部分生效的现象,请在创建使用高级混合模式的过滤器或显示对象之前,选择继承渲染目标分辨率:

import { Filter } from 'pixi.js'; import 'pixi.js/advanced-blend-modes'; Filter.defaultOptions.resolution = 'inherit'; sprite.blendMode = 'overlay';

'inherit'使过滤器按当前渲染目标的分辨率渲染,能提升这类对分辨率敏感的混合效果的真实度;代价是相比默认值1会增加显存占用与运行时开销。

自定义过滤器

内置过滤器不够用时,可以用 GLSL 着色器编写自己的过滤器。PixiJS v8 采用GLSL ES 3.0 风格:用in/out代替attribute/varying,用texture()代替texture2D

使用Filter.from()(推荐快速上手)

最简方式:通常只需提供片元着色器,顶点着色器由 PixiJS 提供默认实现(负责顶点位置计算),传undefined或省略即可使用默认顶点着色器。下面的例子用片元着色器做一个反色效果:

const simpleFilter = Filter.from({ gl: { fragment: ` in vec2 vTextureCoord; out vec4 finalColor; uniform sampler2D uTexture; void main(void) { vec4 color = texture(uTexture, vTextureCoord); finalColor = vec4(1.0 - color.rgb, color.a); // 反色 } `, }, resources: {}, });

从实现看,Filter.from会调用GlProgram.from/GpuProgram.from把着色器源码编译成程序,再包一层Filter(见 Filter.ts#L261-L283)。

若需要同时完全控制顶点与片元着色器,可以写一个"水波"滤镜——片元着色器按正弦波横向偏移采样坐标,uTime随时间递增形成波动动画:

import { Filter } from 'pixi.js'; const waveFilter = Filter.from({ gl: { vertex: ` in vec2 aPosition; out vec2 vTextureCoord; uniform vec4 uInputSize; uniform vec4 uOutputFrame; uniform vec4 uOutputTexture; vec4 filterVertexPosition(void) { vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy; position.x = position.x * (2.0 / uOutputTexture.x) - 1.0; position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z; return vec4(position, 0.0, 1.0); } vec2 filterTextureCoord(void) { return aPosition * (uOutputFrame.zw * uInputSize.zw); } void main(void) { gl_Position = filterVertexPosition(); vTextureCoord = filterTextureCoord(); } `, fragment: ` in vec2 vTextureCoord; out vec4 finalColor; uniform sampler2D uTexture; uniform float uWaveAmplitude; uniform float uWaveFrequency; uniform float uTime; void main(void) { vec2 coord = vTextureCoord; coord.x += sin(coord.y * uWaveFrequency + uTime) * uWaveAmplitude; finalColor = texture(uTexture, coord); } `, }, resources: { waveUniforms: { uWaveAmplitude: { value: 0.05, type: 'f32' }, uWaveFrequency: { value: 10.0, type: 'f32' }, uTime: { value: 0.0, type: 'f32' }, }, }, }); sprite.filters = [waveFilter]; app.ticker.add((ticker) => { waveFilter.resources.waveUniforms.uniforms.uTime += 0.1 * ticker.deltaTime; });

这段代码中resources里的 uniform 会在渲染时自动绑定,无需手动上传。注意gl_Position = filterVertexPosition()vTextureCoord = filterTextureCoord()是默认顶点着色器的标准套路(对应文档中的"着色器约定"一节)。

使用new Filter()配合预编译程序

需要更高控制力时,可以自己构造GlProgram/GpuProgram再传入Filter构造函数:

import { Filter, GlProgram } from 'pixi.js'; const glProgram = new GlProgram({ vertex: vertexSrc, fragment: fragmentSrc }); const filter = new Filter({ glProgram, resources: { timeUniforms: { uTime: { value: 0.0, type: 'f32' }, }, }, });

着色器编写约定

  • 片元着色器使用out vec4 finalColor;输出颜色(不是gl_FragColor);
  • 采样纹理使用texture()不是texture2D);
  • 默认顶点着色器提供filterVertexPosition()filterTextureCoord()辅助函数处理输出帧定位,自定义顶点着色器时可直接复用;
  • resources中声明的 uniform 可通过filter.resources.<组名>.uniforms.<uniform名>在 JS 侧读写(如动画循环里更新uTime)。

[!NOTE] 为了同时支持 WebGL 与 WebGPU 双渲染器,请同时提供glProgram(GLSL)与gpuProgram(WGSL)。仅提供一个时,过滤器在缺少对应程序的那个渲染器上会被跳过、按原样渲染。仓库中内置过滤器均为双实现,例如 BlurFilterPass 同时通过generateBlurGlProgram(GLSL)与generateBlurProgram(WGSL)生成两个程序。更多示例见 examples/mesh_multipass_shader_effects 与 examples/filters_custom-shader_glsl(后者含 custom.frag / custom.vert 可对照)。

过滤器基础选项

所有过滤器(无论内置还是自定义)都接受以下基础选项,定义于 FilterOptions 接口:

选项类型默认值说明
blendModestring'normal'过滤器输出使用的混合模式
resolutionnumber \| 'inherit'1渲染分辨率,越低性能越好、质量越低
paddingnumber0过滤器区域外扩的像素数(模糊等会向外扩散的效果需要 padding 防止裁切)
antialiasFilterAntialias \| boolean'off'抗锯齿模式:'on'/'off'/'inherit'(布尔值会被转换为 on/off,见 Filter.ts#L200-L208)
blendRequiredbooleanfalse是否需要在着色器中读取背景像素(开启后着色器需声明uBackTextureuniform)
clipToViewportbooleantrue是否将过滤器纹理裁剪到视口范围内

其中antialias的三种取值语义:'on'强制抗锯齿、'off'(默认)关闭、'inherit'跟随渲染目标设置。resolution'inherit'见上文高 DPI 场景。

渲染管线视角:过滤器底层做了什么

为了写出高性能的过滤器代码,理解底层流程很有必要。Filter 类的源码注释 完整描述了挂载过滤器后渲染器实际执行的步骤:

  1. 打断当前批次(break the current batch);
  2. getGlobalBounds递归遍历所有子对象,测量目标的大小
  3. 从纹理池获取2 的幂或屏幕尺寸的纹理;
  4. 把目标对象渲染到该纹理(离屏渲染);
  5. 再用过滤器的着色器程序,把该纹理作为 quad渲染回主帧缓冲

某些过滤器(如模糊)需要多趟处理,性能开销会进一步放大。注释中还特别强调了两点经验:瓶颈通常不是着色器本身的复杂度,而是频繁的帧缓冲与着色器切换"一个过滤器作用于一个含大量对象的容器"远快于"大量对象各自挂过滤器"

性能优化实践

  • 限制过滤区域:默认每帧 PixiJS 都根据对象边界计算过滤区域。手动设置sprite.filterArea为固定Rectangle可跳过该计算并缩小处理范围。
  • 共享过滤器实例:同一个过滤器实例可以挂到多个对象上,避免重复创建与重复上传资源。
  • 用不到就移除sprite.filters = null可完全跳过过滤处理。
  • 调节质量:降低BlurFilterquality可减少趟数、显著提速。
  • 优先使用图集/烘焙:对静态效果,应把效果烘焙进纹理,而不是运行时用过滤器。
import { Rectangle, BlurFilter } from 'pixi.js'; // 把过滤处理限制在 200x200 区域内 sprite.filterArea = new Rectangle(0, 0, 200, 200); // 在多个精灵之间共享同一个模糊实例 const sharedBlur = new BlurFilter({ strength: 4 }); sprite1.filters = [sharedBlur]; sprite2.filters = [sharedBlur];

相关源码与延伸阅读

想继续深入,可在当前仓库中查看以下入口:

  • Filter 基类与 FilterOptions:过滤器选项、Filter.from、默认行为
  • FilterSystem:过滤器的渲染调度系统
  • 五个内置过滤器:Alpha、Blur、ColorMatrix、Displacement、Noise 各自的实现与默认值
  • 高级混合模式:21 种混合模式的类实现与注册逻辑 init.ts
  • BlurFilter 相关测试、ColorMatrixFilter 测试:验证各参数行为与渲染结果
  • 可运行示例:examples/filters_blur.ts、examples/filters_color-matrix.ts、examples/filters_displacement.ts、examples/filters_displacement_interactive.ts、examples/filters_custom-shader_glsl

【免费下载链接】pixijsThe HTML5 Creation Engine: Create beautiful digital content with the fastest, most flexible 2D WebGL renderer.项目地址: https://gitcode.com/gh_mirrors/pi/pixijs

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 4:42:09

围绕 AGENTS.md 做上下文预算,TaoToken 的 Base URL 一处填写

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 4:40:24

StarRocks DROP STORAGE VOLUME 详解:语法、权限与删除保护机制

StarRocks DROP STORAGE VOLUME 详解&#xff1a;语法、权限与删除保护机制 【免费下载链接】starrocks The worlds fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRock…

作者头像 李华
网站建设 2026/9/19 4:36:34

嵌入式C中printf终端去哪了?MicroLIB标准IO重定向详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 4:36:07

高通AR1+变色镜片:AR眼镜供应链BOM与功耗散热拆解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 4:34:07

Obsidian+Claude Code:打造从素材收集到成稿输出的内容工厂

最近我在搭自己的内容生产系统时&#xff0c;把 Obsidian 和 Claude Code 组合到了一条流水线上&#xff0c;跑通了从素材收集到成稿输出的完整流程。这一套搭配很值得记录&#xff0c;因为 Obsidian 负责本地知识库的沉淀、双向链接和插件生态&#xff0c;Claude Code 能在命令…

作者头像 李华