10

Java でタイル ベースのエンジンのライティングを行う最も効率的な方法は何ですか?
タイルの後ろに黒い背景を置いて、タイルのアルファを変更することでしょうか?
または、黒の前景を置き、そのアルファを変更しますか? それとも何か?

これは私が望む種類の照明の例です:
http://i.stack.imgur.com/F5Lzo.png

4

3 に答える 3

25

これを達成する方法はたくさんあります。最終決定を下す前に、少し時間をかけてください。使用することを選択できるいくつかのテクニックを簡単に要約し、最後にいくつかのコードを提供します。


ハードライティング

ハードエッジの照明効果 (例の画像など) を作成したい場合は、いくつかのアプローチが思い浮かびます。

ハードライトの例

速くて汚い(あなたが提案したように)

  • 黒の背景を使用する
  • さの値に応じてタイルのアルファ値を設定します

問題は、タイルを以前よりも明るくすることも (ハイライト)、ライトの色を変更することもできないことです。これらはどちらも、通常、ゲームの照明を美しく見せる側面です。

タイルの 2 番目のセット

  • (黒/色付き) タイルの 2 番目のセットを使用します。
  • これらをメインタイルの上に置きます
  • 新しい色の強さに応じて、新しいタイルのアルファ値を設定します。

このアプローチには、最初のアプローチと同じ効果があり、オーバーレイ タイルを黒以外の色で着色できるという利点があります。これにより、色付きのライトとハイライトの両方が可能になります。

例: 黒いタイルを使ったハードライトの例

簡単ですが、問題は、これが非常に非効率的な方法であることです。(タイルごとに 2 つのレンダリングされたタイル、一定の色変更、多くのレンダリング操作など)


より効率的なアプローチ (ハードおよび/またはソフト照明)

あなたの例を見ると、光は常に特定のソース タイル (キャラクター、トーチなど) から来ると思います。

  • すべてのタイプのライト (大きなトーチ、小さなトーチ、キャラクター ライティング) に対して、ソース タイル (ライト マスク) に対する特定のライティング動作を表すイメージを作成します。トーチの場合は次のようになります (白はアルファ):

中心光マスク

  • 光源であるタイルごとに、この画像をソースの位置にオーバーレイとしてレンダリングします。
  • 少し明るい色を追加するには、完全なアルファの代わりに、たとえば 10% の不透明なオレンジを使用できます。

結果

イメージ マスク ハード ライトの結果

柔らかな光を加える

ソフト ライトは大した問題ではありません。タイルと比較して、ライト マスクの詳細を使用するだけです。通常は黒い領域で 15% のアルファのみを使用することで、タイルが照らされていないときにローサイト効果を追加できます。

やわらかい光

マスク イメージを変更するだけで、より複雑な照明フォーム (コーンなど) を簡単に実現できます。

複数の光源

複数の光源を組み合わせる場合、この方法では問題が発生します。互いに交差する 2 つのマスクを描画すると、マスク自体が相殺される可能性があります。

マスク解除

私たちが望んでいるのは、ライトを差し引くのではなく、ライトを追加することです。問題の回避:

  • すべてのライト マスクを反転します (アルファは暗い領域、不透明は明るい領域)。
  • これらすべてのライト マスクを、ビューポートと同じサイズの一時的なイメージにレンダリングします。
  • シーナリー全体で新しいイメージを反転してレンダリングします (それが唯一のライト マスクであるかのように)。

これにより、次のような結果になります。 結果画像

mask invert メソッドのコード

最初にすべてのタイルをレンダリングすると仮定BufferedImageして、最後に示した方法に似たガイダンス コードをいくつか提供します (グレースケール サポートのみ)。

たとえばトーチとプレーヤーの複数のライト マスクは、次のように組み合わせることができます。

public BufferedImage combineMasks(BufferedImage[] images)
{
    // create the new image, canvas size is the max. of all image sizes
    int w, h;

    for (BufferedImage img : images)
    {
        w = img.getWidth() > w ? img.getWidth() : w;
        h = img.getHeight() > h ? img.getHeight() : h;
    }

    BufferedImage combined = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);

    // paint all images, preserving the alpha channels
    Graphics g = combined.getGraphics();

    for (BufferedImage img : images)
        g.drawImage(img, 0, 0, null);

    return combined;
}

最終的なマスクは、次のメソッドで作成および適用されます。

public void applyGrayscaleMaskToAlpha(BufferedImage image, BufferedImage mask)
{
    int width = image.getWidth();
    int height = image.getHeight();

    int[] imagePixels = image.getRGB(0, 0, width, height, null, 0, width);
    int[] maskPixels = mask.getRGB(0, 0, width, height, null, 0, width);

    for (int i = 0; i < imagePixels.length; i++)
    {
        int color = imagePixels[i] & 0x00ffffff; // Mask preexisting alpha

        // get alpha from color int
        // be careful, an alpha mask works the other way round, so we have to subtract this from 255
        int alpha = (maskPixels[i] >> 24) & 0xff;
        imagePixels[i] = color | alpha;
    }

    image.setRGB(0, 0, width, height, imagePixels, 0, width);
}

前述のとおり、これは基本的な例です。カラーブレンディングの実装は、もう少し手間がかかるかもしれません。

于 2013-12-09T19:16:57.323 に答える