3

背景情報

HLSLの学習を始めたばかりで、インターネットから学んだことを、簡単な2DXNA4.0弾幕ゲームを作成してテストすることにしました。

弾丸の色を変えるためにピクセルシェーダーを作成しました。

アイデアは次のとおりです。弾丸の元のテクスチャは主に黒、白、赤です。私のピクセルシェーダーの助けを借りて、弾丸ははるかにカラフルにすることができます。

アイディア

ただし、XNA 4.0のspriteBatchにシェーダーを適用する方法とタイミング、および終了するタイミングはわかりません。これが問題の原因である可能性があります。XNA 3.xにはpass.begin()とpass.end()がありましたが、XNA 4.0のpass.apply()は私を混乱させます。

また、renderTargetを使用するのは初めてです。問題が発生する可能性があります。

症状

これは機能しますが、箇条書きリストに同じ色の箇条書きがある場合に限ります。異なる色の弾丸がレンダリングされると、間違った色が生成されます。

ピクセルシェーダーは弾丸テクスチャには適用されていないようですが、レンダリングされたすべての弾丸を含むrenderTargetに適用されているようです。

例: スクリーンショット ここにいくつかの赤い弾丸と青い弾丸があります。最後に作成された弾丸は青い弾丸です。ピクセルシェーダーが赤いものに青い色を追加し、青紫色になっているようです。

弾丸を作成し続けると、赤い弾丸が赤と青紫の間で切り替わっているように見えます。(青いものも切り替わっていると思いますが、はっきりしていません。)

コード

私はHLSLを初めて使用するため、何を提供する必要があるのか​​よくわかりません。これが私が信じている、または問題に関連しているかどうかわからないすべてのことです。

C#-敵の弾丸(または単に弾丸):

protected SpriteBatch spriteBatch;
protected Texture2D texture;
protected Effect colorEffect;
protected Color bulletColor;
... // And some unrelated variables

public EnemyBullet(SpriteBatch spriteBatch, Texture2D texture, Effect colorEffect, BulletType bulletType, (and other data, like velocity)
{
    this.spriteBatch = spriteBatch;
    this.texture = texture;
    this.colorEffect = colorEffect;
    if(bulletType == BulletType.ARROW_S)
    {
        bulletColor = Color.Red;   // The bullet will be either red
    }
    else
    {
        bulletColor = Color.Blue;  // or blue.
    }
}

public void Update()
{
    ... // Update positions and other properties, but not the color.
}

public void Draw()
{
    colorEffect.Parameters["DestColor"].SetValue(bulletColor.ToVector4());
    int l = colorEffect.CurrentTechnique.Passes.Count();
    for (int i = 0; i < l; i++)
    {
        colorEffect.CurrentTechnique.Passes[i].Apply();
        spriteBatch.Draw(texture, Position, sourceRectangle, Color.White, (float)Math.PI - rotation_randian, origin, Scale, SpriteEffects.None, 0.0f);
    }
}

C#-弾丸マネージャー:

private Texture2D bulletTexture;

private List<EnemyBullet> enemyBullets;
private const int ENEMY_BULLET_CAPACITY = 10000;

private RenderTarget2D bulletsRenderTarget;

private Effect colorEffect;

...

public EnemyBulletManager()
{
    enemyBullets = new List<EnemyBullet>(ENEMY_BULLET_CAPACITY);
}

public void LoadContent(ContentManager content, SpriteBatch spriteBatch)
{
    bulletTexture = content.Load<Texture2D>(@"Textures\arrow_red2");

    bulletsRenderTarget = new RenderTarget2D(spriteBatch.GraphicsDevice, spriteBatch.GraphicsDevice.PresentationParameters.BackBufferWidth, spriteBatch.GraphicsDevice.PresentationParameters.BackBufferHeight, false, SurfaceFormat.Color, DepthFormat.None);

    colorEffect = content.Load<Effect>(@"Effects\ColorTransform");
    colorEffect.Parameters["ColorMap"].SetValue(bulletTexture);
}

public void Update()
{
    int l = enemyBullets.Count();
    for (int i = 0; i < l; i++)
    {
        if (enemyBullets[i].IsAlive)
        {
            enemyBullets[i].Update();
        }
        else
        {
            enemyBullets.RemoveAt(i);
            i--;
            l--;
        }
    }
}

// This function is called before Draw()
public void PreDraw()
{
    // spriteBatch.Begin() is called outside this class, for reference:
    // spriteBatch.Begin(SpriteSortMode.Immediate, null);

    spriteBatch.GraphicsDevice.SetRenderTarget(bulletsRenderTarget);
    spriteBatch.GraphicsDevice.Clear(Color.Transparent);

    int l = enemyBullets.Count();
    for (int i = 0; i < l; i++)
    {
        if (enemyBullets[i].IsAlive)
        {
            enemyBullets[i].Draw();
        }
    }

    spriteBatch.GraphicsDevice.SetRenderTarget(null);
}

public void Draw()
{
    // Before this function is called,
    // GraphicsDevice.Clear(Color.Black);
    // is called outside.

    spriteBatch.Draw(bulletsRenderTarget, Vector2.Zero, Color.White);

    // spriteBatch.End();
}

// This function will be responsible for creating new bullets.
public EnemyBullet CreateBullet(EnemyBullet.BulletType bulletType, ...)
{
    EnemyBullet eb = new EnemyBullet(spriteBatch, bulletTexture, colorEffect, bulletType, ...);
    enemyBullets.Add(eb);
    return eb;
}

HLSL-Effects \ ColorTransform.fx

float4 DestColor;

texture2D ColorMap;
sampler2D ColorMapSampler = sampler_state
{
    Texture = <ColorMap>;
};

struct PixelShaderInput
{
    float2 TexCoord : TEXCOORD0;
};

float4 PixelShaderFunction(PixelShaderInput input) : COLOR0
{
    float4 srcRGBA = tex2D(ColorMapSampler, input.TexCoord);

    float fmax = max(srcRGBA.r, max(srcRGBA.g, srcRGBA.b));
    float fmin = min(srcRGBA.r, min(srcRGBA.g, srcRGBA.b));
    float delta = fmax - fmin;

    float4 originalDestColor = float4(1, 0, 0, 1);
    float4 deltaDestColor = originalDestColor - DestColor;

    float4 finalRGBA = srcRGBA - (deltaDestColor * delta);

    return finalRGBA;
}

technique Technique1
{
    pass ColorTransform
    {
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}

誰かが問題を解決するのを手伝ってくれるなら、私はありがたいです。(または、シェーダーを最適化します。HLSLについてはほとんど知りません。)

4

1 に答える 1

2

XNA 4 では、 Shawn Hargreaves のブログで説明されているように、エフェクトを直接 SpriteBatch に渡す必要があります。

bulletsRenderTargetとは言っても、弾丸を にレンダリングした後、同じ spriteBatch を使用してその RenderTarget を描画し、最後の効果がまだ動作していることに問題があるように私には思えます。これで、画像全体が青く塗られている理由が説明できます。

解決策は、SpriteBatch の 2 つの Begin()/End() パスを使用することです。1 つはエフェクトあり、もう 1 つはエフェクトなしです。または、最初から別の RenderTarget を使用しないでください。この場合は無意味に思えます。

私はピクセルシェーダーの初心者でもあるので、2cだけです。

于 2012-06-03T04:10:24.667 に答える