LoginSignup
0
1

More than 5 years have passed since last update.

GlobalZOrder を使用する環境下での RenderTexture の扱い

Posted at

古い端末では DrawCall 数の重要さをひしひしと感じる今日このごろ。お元気でしょうか。
この記事では、そんなときに遭遇した不思議な RenderTexture にまつわる不思議な現象とその対策についてメモしたものです。

GlobalZOrder を弄ると RenderTexture に描画されない…?

GlobalZOrder は親子関係を無視して描画順を入れ替えることができるので、プレイヤーノードの中の HP バーだけ前面に表示したい場合や、必ず裏側に回る背景、ポップアップウインドウを実装するために、GlobalZOrder を使うと思います。
(親要素だけにGlobalZOrder を指定するだけじゃ子要素以降には適用されないので、親子関係を考慮して微妙に値を足しつつ再帰的に GlobalZOrder を設定する必要がある面倒臭さがあるのですが。)
自分の場合、裏側に回ったポップアップウインドウを RenderTexture に焼き付けてまとめる処理を書いていました。
これらをスクリーン上に描画する際は問題無いと思います。
ですが、これを RenderTexture に描画しようとすると、おかしな現象が発生します。

例えば、次のようなコードがあったとします。

// RenderTexture の準備
auto *renderTexture = cocos2d::RenderTexture::create(200, 300);
addChild(renderTexture);

// RenderTexture の内容を確認するため中央に寄せておく
auto &screenSize = cocos2d::Director::getInstance()->getOpenGLView()->getDesignResolutionSize();
renderTexture->setPosition(cocos2d::Vec2(screenSize.width * 0.5f, screenSize.height * 0.5f));

// RenderTexture に描画したい Sprite の準備
auto *sprite = cocos2d::Sprite::create("hoge.png");
sprite->setPosition(cocos2d::Vec2(100.0f, 150.0f));
sprite->setGlobalZOrder(100.0f);

// レンダリング対象は visit を呼び出す直前に isVisible == true であれば
// 適当な場所に addChild されていても問題ない。
container->addChild(sprite);

// RenderTexture に描画
renderTexture->begin();
sprite->visit();
renderTexture->end();

// RenderTexture には焼きこんだので、結果がわかりやすいように非表示にしておく。
sprite->setVisible(false);

このコードを実行すると、hoge.png が一瞬画面にちらつくように表示された後、RenderTexture には何も描画されずに画面上に何も表示されなくなると思います。

原因

この現象は cocos2d-x 3.x から描画処理を非同期に行うようになったことに関係してます。
まず、RenderTexture の GlobalZOrder はデフォルトの 0.0f なので、begin 命令及び end 命令の優先度はデフォルトのままです。
ところが、sprite の GlobalZOrder は 100.0f に設定されているため、通常より前面に描画するように、つまり GlobalZOrder が 100.0f 未満のものが処理された後に sprite の visit() 内部で発行された描画命令が走ります。
すると実行順は

  1. RenderTexture への描画開始
  2. RenderTexture への描画終了
  3. spriteの描画

となってしまいます。
このため、spriteの描画の時点では画面が描画対象となっている状態なので sprite が描画されてしまいました。

対処方法

RenderTexture への描画開始/終了命令が先に来ているのが原因なので、spriteの描画の前に RenderTexture への描画開始命令を、spriteの描画の後に RenderTexture への描画終了命令が来るようにすることで解決できます。
そのために、RenderTexture への描画処理を以下のように書き換えることでうまくいきます。

// RenderTexture に描画
renderTexture->setGlobalZOrder(-65536.0f);
renderTexture->begin();
sprite->visit();
renderTexture->setGlobalZOrder(65536.0f);
renderTexture->end();

このようにすると、RenderTexture への描画命令の順番が強制的に変更され無事に

  1. RenderTexture への描画開始
  2. spriteの描画
  3. RenderTexture への描画終了

の順で実行されるようになります。
今回は 65535/-65536 という値を使いましたが、もしかしたら std::numeric_limits::max や infinity も使えるかもしれません。

それでは良い RenderTexture ライフを。

0
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
0
1