【问题标题】:Prevent moire pattern in pixi.js防止 pixi.js 中的莫尔条纹
【发布时间】:2020-08-06 05:54:01
【问题描述】:

我正在尝试使用 pixi.js 创建一个可缩放的画布,其中矩形排列在网格中。一切都很顺利,除了网格会产生严重的波纹效果。我对 pixjs 和 webgl 的了解只是非常肤浅,但我怀疑抗锯齿的某些东西没有像我预期的那样工作。我正在使用预先在单独的画布中创建的 2048x2048px 纹理绘制矩形。我把它做得那么大,所以我这样做是为了让我可以一直放大,同时仍然有一个清晰的矩形。我也尝试过使用app.renderer.generateTexture(graphics),但得到了类似的结果。

黑色矩形是使用 pixi.js 绘制的,红色矩形是使用 SVG 作为参考绘制的。 SVG 中仍然会出现波纹,但要少得多。有什么想法可以让我更接近 SVG 版本的样子吗?你可以找到一个工作版本here。

这是我用来设置 pixi.js 应用程序的相关代码:

// PIXI SETUP

const app = new Application({
  view: canvasRef,
  width,
  height,
  transparent: true,
  antialias: false,
  autoDensity: true,
  resolution: devicePixelRatio,
  resizeTo: window
});

const particleContainer = new ParticleContainer(2500, {
  scale: true,
  position: true,
  rotation: true,
  uvs: true,
  alpha: true
});

app.stage.addChild(particleContainer);

// TEXTURE

const size = 2048;
const canvas = document.createElement("canvas");
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, size, size);
ctx.fill();
const texture = PIXI.Texture.from(canvas);

// RECTANGLE GRID

const size = 10;
for(let i=0; i<2500; i++) {
  const particle = Sprite.from(texture);
  particle.x = i % 50 * size * 1.5;
  particle.y = Math.floor(i / 50) * size * 1.5;
  particle.anchor.set(0);
  particle.width = size;
  particle.height = size;
  parent.addChild(particle);
}

【问题讨论】:

标签: javascript html5-canvas pixi.js


【解决方案1】:

不渲染亚像素细节。

在避免伪影的同时维护网格的最佳方法是不渲染低于画布分辨率的网格步长。例如,如果您缩小了 100,则不要绘制小于 100 像素的网格。

由于这可能会导致网格步长弹出和弹出,您可以根据缩放级别淡入和淡出网格。

该示例显示了执行此操作的一种方法。它仍然有一些伪影,这些是难以避免的,但这消除了在每个缩放级别渲染所有细节时出现的可怕的波纹图案。

网格被定义为 2D 重复模式以减少渲染开销。

我还发现网格线比网格方块更成问题(Demo 两者都有)

这是非常基本的,可以适应任何类型的网格布局。

requestAnimationFrame(mainLoop);
const ctx = canvas.getContext("2d");
const size = 138;
const grids = createPatterns(size, 4, "#222", "#EEE", "#69B", "#246");
var zoom = 1;
var zoomTarget = 16;
var zoomC = 0;
var gridType = 0;
var origin = {x: ctx.canvas.width / 2, y: ctx.canvas.height / 2};
const scales = [0,0,0];

function createPatterns(size, lineWidth, color1, color2, color3, color4){
    function grid(col1, col2) {
        ctx.fillStyle = col1;
        ctx.fillRect(0, 0, size, size);
        ctx.fillStyle = col2;
        ctx.fillRect(0, 0, size, lineWidth);
        ctx.fillRect(0, 0, lineWidth, size);
    }
    function grid2(col1, col2) {
        ctx.fillStyle = col1;
        ctx.fillRect(0, 0, size, size);
        ctx.fillStyle = col2;
        ctx.fillRect(0, 0, size / 2, size / 2);
        ctx.fillRect( size / 2, size / 2, size / 2, size / 2);

    }
    const patterns = [];
    const ctx = Object.assign(document.createElement("canvas"), {width: size, height: size}).getContext("2d");
    grid(color1, color2)
    patterns[0] =  ctx.createPattern(ctx.canvas, "repeat");
    grid2(color3, color4)
    patterns[1] =  ctx.createPattern(ctx.canvas, "repeat");
    return patterns;
}


function drawGrid(ctx, grid,  zoom, origin, smooth = true) {
    function zoomAlpha(logScale) {
        const t = logScale % 3;
        return  t < 1 ? t % 1 : t > 2 ? 1 - (t - 2) % 1 : 1;
    }
    function fillScale(scale) {
        ctx.setTransform(scale / 8, 0, 0, scale / 8, origin.x, origin.y);
        ctx.globalAlpha = zoomAlpha(Math.log2(scale));
        ctx.fill();
    }
    ctx.fillStyle = grid;
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.imageSmoothingEnabled = smooth;
    ctx.beginPath();
    ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);    
    ctx.globalAlpha = 1;
    const l2 = Math.log2(zoom);
    
    scales[0] = 2 ** ((l2 + 122) % 3); //  zoom limit 1 / (2 ** 122) (well beyond number precision)
    scales[1] = 2 ** ((l2 + 123) % 3); 
    scales[2] = 2 ** ((l2 + 124) % 3);    
    scales.sort((a,b) => a - b);
    
    fillScale(scales[0]);
    fillScale(scales[1]);
    fillScale(scales[2]);
    ctx.globalAlpha = 1;
}

function mainLoop() {
    if (innerWidth !== ctx.canvas.width || innerHeight !== ctx.canvas.height) {
        origin.x = (ctx.canvas.width = innerWidth) / 2;
        origin.y = (ctx.canvas.height = innerHeight) / 2;
        zoomTarget = 16;
        zoom = 1;
    }
    zoomC += (zoomTarget - zoom) * 0.3;    
    zoomC *= 0.02;
    zoom += zoomC;
    if (gridType === 0) {
        drawGrid(ctx, grids[0], zoom, origin);
    } else {
        drawGrid(ctx, grids[1], zoom, origin, false);
    }
    requestAnimationFrame(mainLoop);
}
zoomIn.addEventListener("click", () => zoomTarget *= 2);
zoomOut.addEventListener("click", () => zoomTarget *= 1/2);
toggle.addEventListener("click", () => gridType = (gridType + 1) % 2);
* { margin: 0px;}
canvas { position: absolute; top: 0px;left: 0px; }
.UI { position: absolute; top: 14px; left: 14px; }
<canvas id="canvas"></canvas>
<div class="UI">
<button id="zoomIn">Zoom In</button><button id="zoomOut">Zoom Out</button><button id="toggle">Toggle grid type</button>
</div>

【讨论】:

  • 听起来很有希望!我试试这个
猜你喜欢
  • 1970-01-01
  • 2012-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-20
  • 2018-07-16
相关资源
最近更新 更多