1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

奇数サイズかつ起点が中央である画像がぼやける問題を修正してみた

Last updated at Posted at 2020-02-26

大本の問題についてはこちらです。
https://paradre.com/ピクチャがぼやける問題とその対策【ツクールmv】/

#リンク先にある通り画像サイズを偶数にすればいいのでは?
その通りですが、この解が適用できないパターンがいくつかあります。

  • 画像が多すぎて一々修正していられない
  • 回転等の関係から、基点(anchor)を中央(0.5)ではなく、0.7等半端な位置に設定せざるを得ない

これらの状況に対処する為、スクリプト側で一括で何とかする法がないかを前々から考えておりました。
#修正法
発見の経緯は後に述べるとして、まずは実修正法を。

※pixi.jsやrpg_core.jsの改変になるので__事前に必ずバックアップ__してください。プロジェクト壊れても責任は取れません…
※需要が多いならプラグイン化も試みます。

###roundPixelsを有効化

rpg_core.js
Graphics._createRenderer = function() {
    PIXI.dontSayHello = true;
    var width = this._width;
    var height = this._height;
    var options = { view: this._canvas };

rpg_core.js
Graphics._createRenderer = function() {
    PIXI.dontSayHello = true;
    var width = this._width;
    var height = this._height;
    var options = { view: this._canvas, roundPixels: true}; //ここに追加パラメーター入れ

これでSprite系自体は治ります。ピクチャのみ別枠で処理をする必要があります。

###PictureRendererに於いて、roundPixelsを参照するように

pixi-picture.js
PictureRenderer.prototype._renderSprite = function (sprite, shader) {
                var renderer = this.renderer;
                var quad = shader.tempQuad;
                renderer.bindVao(quad.vao);
                var uvs = sprite.texture._uvs;
                var vertices = quad.vertices;
                var vd = sprite.vertexData;
                for (var i = 0; i < 8; i++) {
                    quad.vertices[i] = vd[i];
                }

pixi-picture.js
PictureRenderer.prototype._renderSprite = function (sprite, shader) {
                var renderer = this.renderer;
                var quad = shader.tempQuad;
                renderer.bindVao(quad.vao);
                var uvs = sprite.texture._uvs;
                var vertices = quad.vertices;
                var vd = sprite.vertexData;
                for (var i = 0; i < 8; i++) {
                    if (renderer.roundPixels)
                    {
                        quad.vertices[i] = Math.floor(vd[i]);
                    }
                    else
                    {
                        quad.vertices[i] = vd[i];
                    }
                }

これで一応の効果は見られるはずです。
ただ、どうやらピクチャがタイル状になるパターンもある?らしく、そちらを管轄しているのは
PictureRenderer.prototype._renderWithShader
修正する場合は同様に

pixi-picture.js
vertices[0] = (a * w1) + (c * h1) + tx;
vertices[1] = (d * h1) + (b * w1) + ty;
vertices[2] = (a * w0) + (c * h1) + tx;
vertices[3] = (d * h1) + (b * w0) + ty;
vertices[4] = (a * w0) + (c * h0) + tx;
vertices[5] = (d * h0) + (b * w0) + ty;
vertices[6] = (a * w1) + (c * h0) + tx;
vertices[7] = (d * h0) + (b * w1) + ty;

この各項をifで分岐し、Math.floorで囲めば宜しいかと。

###副作用
fukuさんにご指摘いただきました。

上記の修正はMath.floorを使っているのですが、通常のSpriteRenderer|0によって疑似的にMath.floorを行っているため、

(但し、当方の方で実験してみた所、スピードを1/40まで引き延ばし、0位置だけ80フレームに1ピクセル動く形にしても体感できる程の差は出ませんでした。
こちらは個人差もあると考えますので私は修正しませんでしたが、直す場合はpixi.js内、SpriteRendererのroundPixelsを検索し、その中にある|0を消去した上で、Math.floorで囲んで代替すれば行けるかと考えます)

#修正法発見の経緯
###きっかけ
コアスクリプトを読んでいた所偶然このような物を発見しました。

pixi.js
    // eslint-disable-next-line valid-jsdoc
    /**
     *
     * @param {object} [options] - The optional renderer parameters
     * @param {number} [options.width=800] - the width of the screen
     * @param {number} [options.height=600] - the height of the screen
     * @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
     * @param {boolean} [options.transparent=false] - If the render view is transparent, default false
     * @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
     * @param {boolean} [options.antialias=false] - sets antialias. If not available natively then FXAA
     *  antialiasing is used
     * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native.
     *  FXAA is faster, but may not always look as great
     * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer.
     *  The resolution of the renderer retina would be 2.
     * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear
     *  the canvas or not before the new render pass. If you wish to set this to false, you *must* set
     *  preserveDrawingBuffer to `true`.
     * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation,
     *  enable this if you need to call toDataUrl on the webgl context.
     * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when
     *  rendering, stopping pixel interpolation.
     * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
     * with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
     */
    function WebGLRenderer(options, arg2, arg3) {
        _classCallCheck(this, WebGLRenderer);

[options.roundPixels=false]に対しての説明がIf true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation.
(trueにすればPixiがx/y値にMath.floorを実行し、ピクセルが補間されることを止めます)

とある。で、あれば。
「これをtrueにする事で一括で問題は解決するのではないか?」と考えました。

###だが然し現実は非情であった
ピクチャを用いた実験の結果、依然として画像はぼやけていたのでした
pixiのgitを検索した結果、確かに旧バージョンではroundPixelsが効かないバグがあったのだが、それはツクールMV以前に直されていたはず。
SpriteRendererの書き方が悪いのか?といろいろ確認を試みるが、該当の個所は整数で間違いはない。

###転機
諦めかけていたその時、ツイッターから華麗なツッコミが舞い込む。

!?

この線でPictureRendererを解析し、verticesに当たる部分にMath.floorを掛けた所、無事画像がシャープになりました。

#スペシャルサンクス
解決法のヒントと副作用の指摘を頂いた fuku 様
https://twitter.com/fuku_fgs

素材提供と実テスト、比較テストを行っていただいた サイリ 様
https://twitter.com/sairi55

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?