大本の問題についてはこちらです。
https://paradre.com/ピクチャがぼやける問題とその対策【ツクールmv】/
#リンク先にある通り画像サイズを偶数にすればいいのでは?
その通りですが、この解が適用できないパターンがいくつかあります。
- 画像が多すぎて一々修正していられない
- 回転等の関係から、基点(anchor)を中央(0.5)ではなく、0.7等半端な位置に設定せざるを得ない
これらの状況に対処する為、スクリプト側で一括で何とかする法がないかを前々から考えておりました。
#修正法
発見の経緯は後に述べるとして、まずは実修正法を。
※pixi.jsやrpg_core.jsの改変になるので__事前に必ずバックアップ__してください。プロジェクト壊れても責任は取れません…
※需要が多いならプラグイン化も試みます。
###roundPixelsを有効化
Graphics._createRenderer = function() {
PIXI.dontSayHello = true;
var width = this._width;
var height = this._height;
var options = { view: this._canvas };
↓
Graphics._createRenderer = function() {
PIXI.dontSayHello = true;
var width = this._width;
var height = this._height;
var options = { view: this._canvas, roundPixels: true}; //ここに追加パラメーター入れ
これでSprite系自体は治ります。ピクチャのみ別枠で処理をする必要があります。
###PictureRendererに於いて、roundPixelsを参照するように
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];
}
↓
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
。
修正する場合は同様に
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を行っているため、
|0とMath.floorは挙動が違うんですよ。
— fuku@FGS開発(1/18 Fuku Game Studio更新) (@fuku_fgs) February 25, 2020
|0は0方向、floorは負の無限方向なのですが、位置系の丸めは負の無限方向でないと負座標から正座標へ移動させる時に0に合うタイミングだけ-1<x<1と2倍になってしまい、カクッとした感触が出てしまうんです。https://t.co/lthbLAETFf
(但し、当方の方で実験してみた所、スピードを1/40まで引き延ばし、0位置だけ80フレームに1ピクセル動く形にしても体感できる程の差は出ませんでした。
こちらは個人差もあると考えますので私は修正しませんでしたが、直す場合はpixi.js内、SpriteRendererのroundPixels
を検索し、その中にある|0を消去した上で、Math.floorで囲んで代替すれば行けるかと考えます)
#修正法発見の経緯
###きっかけ
コアスクリプトを読んでいた所偶然このような物を発見しました。
// 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)の方は利かないですね。
— fuku@FGS開発(1/18 Fuku Game Studio更新) (@fuku_fgs) February 25, 2020
pixi-picture.jsの方にはroundPixelsの処理がなさそうです。
!?
この線でPictureRendererを解析し、verticesに当たる部分にMath.floor
を掛けた所、無事画像がシャープになりました。
#スペシャルサンクス
解決法のヒントと副作用の指摘を頂いた fuku 様
https://twitter.com/fuku_fgs
素材提供と実テスト、比較テストを行っていただいた サイリ 様
https://twitter.com/sairi55