You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}
`

问题原因

  1. 坐标系统不匹配:PixiJS屏幕坐标Y轴从上到下,WebGL纹理坐标Y轴从下到上,原代码未翻转Y轴导致垂直偏移。
  2. 设备像素比忽略:未考虑屏幕devicePixelRatio,高清屏或缩放场景下坐标转换出现偏差。
  3. Uniform更新方式错误:直接赋值uniforms.uLightPosition而非更新value属性,导致着色器未同步最新值。
  4. 亮度值无限制:鼠标距离超过半径时,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 19:54:50