PixiJS v8自定义滤镜坐标偏移问题求助
问题:PixiJS自定义滤镜中鼠标跟随红色区域偏移的解决办法
问题描述
作为PixiJS及自定义着色器/滤镜新手,尝试为精灵添加滤镜,使鼠标位置附近的像素根据距离变红,但红色区域始终向右下方偏移,且远离纹理原点时偏移更明显。
原代码
main.js
const { Application, Assets, Filter, GlProgram, Sprite, Texture } = PIXI; let app; (async () => { app = new Application(); await app.init({ resizeTo: window, hello: true, preference: 'webgl', }); document.body.appendChild(app.canvas); const texture = await Assets.load('./img/Haus1.png'); const sprite = Sprite.from(texture); app.stage.addChild(sprite); const filter = new Filter({ glProgram: new GlProgram({ fragment, vertex: PIXI.defaultFilterVert }), resources: { localUniforms: { uLightPosition: { value: [0.0, 0.0], type: 'vec2<f32>' }, uLightRadius: { value: 0.1, type: 'f32' }, }, }, }); sprite.filters = [filter]; addEventListener("mousemove", (event) => { const x = (event.clientX - sprite.x) / sprite.width const y = (event.clientY- sprite.y ) / sprite.height filter.resources.localUniforms.uniforms.uLightPosition = [x, y]; }); let pixelRadius = 0.1 addEventListener("wheel", (event) => { if (event.deltaY > 0) { pixelRadius = (pixelRadius * sprite.width + 5) / sprite.width filter.resources.localUniforms.uniforms.uLightRadius = pixelRadius } if (event.deltaY < 0) { pixelRadius = (pixelRadius * sprite.width - 5) / sprite.width filter.resources.localUniforms.uniforms.uLightRadius = pixelRadius } }) })();
Fragment Shader
const fragment = ` in vec2 vTextureCoord; in vec4 vColor; uniform vec2 uLightPosition; uniform float uLightRadius; uniform vec2 uResolution; uniform sampler2D uTexture; void main(void) { float dist = distance(vTextureCoord, uLightPosition); float brightness = dist/uLightRadius; vec4 fg = texture2D(uTexture, vTextureCoord); fg.r = 1.0-brightness; gl_FragColor = fg; } `
问题原因
- 坐标系统不匹配:PixiJS屏幕坐标Y轴从上到下,WebGL纹理坐标Y轴从下到上,原代码未翻转Y轴导致垂直偏移。
- 设备像素比忽略:未考虑屏幕
devicePixelRatio,高清屏或缩放场景下坐标转换出现偏差。 - Uniform更新方式错误:直接赋值
uniforms.uLightPosition而非更新value属性,导致着色器未同步最新值。 - 亮度值无限制:鼠标距离超过半径时,
brightness大于1,红色通道值变为负数,出现异常颜色。
修复方案
修改后的main.js(关键部分)
addEventListener("mousemove", (event) => { // 适配设备像素比,转换鼠标坐标到画布空间 const dpr = app.renderer.resolution; const canvasX = event.clientX * dpr; const canvasY = event.clientY * dpr; // 将画布坐标转为精灵局部坐标,自动处理位置、缩放、锚点 const localPos = sprite.toLocal(new PIXI.Point(canvasX, canvasY)); // 转换为纹理坐标,翻转Y轴适配WebGL纹理系统 const x = localPos.x / sprite.width; const y = 1 - (localPos.y / sprite.height); // 正确更新Uniform的value属性 filter.resources.localUniforms.uLightPosition.value = [x, y]; }); // 滚轮事件修正Uniform更新方式,避免半径为负 addEventListener("wheel", (event) => { if (event.deltaY > 0) { pixelRadius = (pixelRadius * sprite.width + 5) / sprite.width; } else { pixelRadius = Math.max(0.01, (pixelRadius * sprite.width - 5) / sprite.width); } filter.resources.localUniforms.uLightRadius.value = pixelRadius; });
修改后的Fragment Shader
const fragment = ` in vec2 vTextureCoord; in vec4 vColor; uniform vec2 uLightPosition; uniform float uLightRadius; uniform sampler2D uTexture; void main(void) { float dist = distance(vTextureCoord, uLightPosition); // 限制亮度值在0-1区间,避免红色通道异常 float brightness = clamp(dist / uLightRadius, 0.0, 1.0); vec4 fg = texture2D(uTexture, vTextureCoord); fg.r = 1.0 - brightness; gl_FragColor = fg; } `
说明
sprite.toLocal()自动处理精灵的位置、缩放、锚点变换,确保坐标转换精准。- Y轴翻转是适配WebGL纹理坐标的核心操作,解决垂直偏移问题。
- 更新Uniform必须修改
value属性,保证着色器能获取最新参数。 clamp函数限制亮度值范围,避免出现异常颜色。
内容的提问来源于stack exchange,提问作者Julian
相关产品推荐
相关产品推荐

