locked
現在 「ピクセルシェーダで処理中のピクセルが何番目の三角形なのか」 をピクセルシェーダの中にて知る方法はありますか? RRS feed

  • 質問

  • 皆さん、こんにちは。
    「サカポン」と申します。

     

    現在、頂点バッファに5つの三角形を表すデータを入れてそれをモニタに表示しています。
    ピクセルシェーダで処理中のピクセルが何番目の三角形なのかを、ピクセルシェーダの中にて知る方法はありますか?


    下記の環境で作業を行っています。
    Windows Vista Ultimate SP1
    Microsoft Visual C# 2005 Express Edition
    Microsoft XNA Game Studio 2.0


    よろしくお願い致します。

    2008年9月16日 6:05

回答

  • NyaRuRu さん、詳細で丁寧なご回答ありがとうございました。

     

     NyaRuRu さんからの引用

    データの受け渡しであれば,Texture Coordinate Register を使ったほうが良いでしょう.

     

    早速、下記の情報なども参考にして試してみました。

     

    ドキュメント  VertexElementUsage 列挙型 のTextureCoordinate

    ドキュメント VertexElement.UsageIndex プロパティ

    書籍 「Microsoft XNA UNLEASHED グラフィックスとゲーム開発」の372ページ(このページには特定の用途タイプを持たない情報を保存する際の説明がありました)

     

    その結果、「ピクセルシェーダにて処理中のピクセルが何番目の三角形なのか」を判断することが出来るようになりました。

    個人的な感想として、今回の疑問をクリアするための鍵は

    (1) ピクセルシェーダに渡された数値を if文にて判断する際に使った round関数(絶対にこれを使わなければいけない)。

    (2) シェーダに数値を送る際は、頂点の要素として TextureCoordinateを使い、 用途のインデックスを1にする。

    でした。

    ---------------------------------------------------------------

    Takashi SAKAMOTO さん、

    NyaRuRu さん、

    本当にありがとうございました。

     

    これにて失礼します。

    では。

    2008年9月27日 7:44

すべての返信

  • 取り敢えず、どのような用途を考えていらっしゃるのでしょうか?

     

    頂点データやシェーディング、ライティング、テクスチャの影響もあって、常に利用できるとは限らない方法で良いでしょうか?

    それなら、各頂点のPixel Color にデータを乗せるという技が利用できるかと思います。

    恐らくは Alpha 値に1番目の三角形に属する頂点なら1を、2番目の三角形に属する頂点なら2を入れておいて、PixelShader の中で Alpha 値を見て判断するという作業になるのでしょうか。

     

    2008年9月25日 1:35
  • 「Takashi SAKAMOTO 」さん、こんにちは。
    ご回答、ありがとうございます。


    > 取り敢えず、どのような用途を考えていらっしゃるのでしょうか?

    表示する三角形ごとに異なるサンプラを使う(多数のテクスチャ画像を使う)ことが出来るのかな?という、ひとことで言うと好奇心というか、実験心みたいなのが根本です。
     

    > それなら、各頂点のPixel Color にデータを乗せるという技が利用できるかと思います。

    とりあえず自前の頂点フォーマット(Position、Normal、TextureCoordinate、Color)を持つ頂点データを
    準備して、Colorの Alpha値に数値を入れてピクセルシェーダに送り、ピクセルシェーダにて下記のようにすると、、

    if( IN.f4Color.a == 0 ){
        diffuseColor = tex2D(textureSampler[0], IN.textureUV);
    }

    if( IN.f4Color.a == 1 ){
        diffuseColor = tex2D(textureSampler[1], IN.textureUV);
    }

    、、Alpha値としては0と1しか送っていないのに上記のif文のどれにも当てはまらない場合があるみたいなのです。
    具体的には Alpha値として0~1の間の数値が入っているみたいです。


    ということで、ピクセルシェーダで処理中のピクセルが何番目の三角形なのかをはっきりと知る方法があるのかを探しています。

    2008年9月25日 17:19
  • 上手く動作しないですか…適当なサンプルで調査してみます。

     

    ただ、現段階で一点気になったことがあります。

     

    IN.f4Color.a

     

    の f4 は float[4] の意味でしょうか? だとすると、==0 や ==1 では floating point の精度の問題で厳密に一致しない可能性があります。

    round を利用して、the nearest integer に丸める必要があるのではないかと思われます。

    2008年9月26日 2:53
  • 次のようなコードを書いてみました。ベースは DirectX の Sample である Tut03_Matricesです。これに BasicHLSL などを見ながら、Shader Codeを追加してみました。

    Shader 側のコードは、

     

    Code Snippet

    struct PS_OUTPUT
    {
        float4 RGBColor : COLOR0;  // Pixel color   
    };

    PS_OUTPUT RenderScenePS (
        float4 Position   : POSITION,
        float4 Diffuse    : COLOR0,
        float2 TextureUV  : TEXCOORD0)
    {
        PS_OUTPUT Output ;

     Output.RGBColor = Diffuse ;
     if (round (Diffuse.a * 255.0) == 1.0) {
      Output.RGBColor [0] = 1.0 ;
     } else if (round (Diffuse.a * 255.0) == 2.0) {
      Output.RGBColor [1] = 1.0 ;
     }
        return Output ;
    }

    technique RenderSceneWithPS
    {
     pass P0
     {
      PixelShader  = compile ps_2_0 RenderScenePS () ;
     }
    }

     

     

    C++側は、こんな感じです。

     

    Code Snippet

    struct CUSTOMVERTEX
    {
        FLOAT x, y, z; 

        DWORD color; 

    };

    #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)

     

    // snip ...

     

    HRESULT InitGeometry()
    {
     CUSTOMVERTEX g_Vertices[] = {
      { -1.0f,-1.0f, 0.0f, 0x00ff0000, }, //0
      {  1.0f,-1.0f, 0.0f, 0x000000ff, },
      {  0.0f, 1.0f, 0.0f, 0x00ffffff, },

      { -1.0f,-1.0f, 1.0f, 0x01ff0000, }, //1
      {  1.0f,-1.0f, 1.0f, 0x010000ff, },
      {  0.0f, 1.0f, 1.0f, 0x01ffffff, },

      { -1.0f,-1.0f, 2.0f, 0x02ff0000, }, //2
      {  1.0f,-1.0f, 2.0f, 0x020000ff, },
      {  0.0f, 1.0f, 2.0f, 0x02ffffff, },
     };

        if (FAILED (g_pd3dDevice->CreateVertexBuffer (ARRAYSIZE (g_Vertices) * sizeof (CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL))) {
            return E_FAIL ;
        }

        VOID* pVertices ;
     if( FAILED (g_pVB->Lock( 0, sizeof(g_Vertices), (void**)&pVertices, 0)))
      return E_FAIL ;
     memcpy (pVertices, g_Vertices, sizeof (g_Vertices)) ;
     g_pVB->Unlock () ;
     return S_OK ;
    }

     

    // snip ...

    VOID Render()
    {

      //...


     

      g_pEffect->SetTechnique ("RenderSceneWithPS") ;
      g_pEffect->Begin (&cPasses, 0) ;
            for (iPass = 0 ; iPass < cPasses ; iPass ++) {
       g_pEffect->BeginPass(iPass) ;
       g_pd3dDevice->DrawPrimitive (D3DPT_TRIANGLELIST, 0, 3) ;
       g_pEffect->EndPass () ;
      }
      g_pEffect->End () ;

     

      //...

      return ;

    }

     

     

     

    一応、動作しているように見えるのですが…。

     

    2008年9月26日 10:41
  • 「Takashi SAKAMOTO 」さん、ご回答ありがとうございます。
    返信がかなり遅れてしまい申し訳ありません。

    > IN.f4Color.a
    >の f4 は float[4] の意味でしょうか?

    説明が足りずに迷惑をかけてしまいました。
    「 IN.f4Color.a 」はシェーダ側のコードとして、
    float4 f4Color     : COLOR0;
    としています。


    >だとすると、==0 や ==1 では floating point の精度の問題で厳密に一致しない可能性があります。
    >round を利用して、the nearest integer に丸める必要があるのではないかと思われます。

    早速、お教えいただいた round関数を使い下記のように試してみました。

     if( round(IN.f4Color.a) == 0.0 ){
         diffuseColor = tex2D(textureSampler[0], IN.textureUV);
     }

     if( round(IN.f4Color.a) == 1.0 ){
         diffuseColor = tex2D(textureSampler[1], IN.textureUV);
     }

     if( round(IN.f4Color.a) == 2.0 ){
         diffuseColor = tex2D(textureSampler[2], IN.textureUV);
     }

    、、が、うまくいきません。
    Alpha値として渡した 0.0 と 1.0 は if文を通ってくれるのですが、2.0 には無反応です。
    といいますか、渡した 2.0 は 1.0 として if文を通ってしまいます。
    1より大きな数値は 1 となってしまう状況です。
    ちなみに、頂点シェーダでは渡した数値 0.0 、1.0 、2.0 をちゃんとif文で判断させることが出来ます。
    ピクセルシェーダに渡す時に数値が 0.0~1.0 の間の数値に変わってしまっています。

    自前の頂点フォーマットのコードは下記の通りです。
    (環境は Microsoft Visual C# 2005 Express Edition、Microsoft XNA Game Studio 2.0 です)

    (コードをうまく投稿することが出来ずくやしいです...見づらくてすいません。)

     public struct VertexPositionNormalTextureName
     {
         public Vector3 Position;
         public Vector3 Normal;
         public Vector2 TextureCoordinate;
         public Vector4 TextureNo;

         public VertexPositionNormalTextureName(Vector3 Position, Vector3 Normal,  Vector2 TextureCoordinate, Vector4 TextureNo)
         {
             this.Position = Position;
             this.Normal = Normal;
             this.TextureCoordinate = TextureCoordinate;
             this.TextureNo = TextureNo;
         }

         public static VertexElement[] VertexElements = {
             new VertexElement( 0,               0, VertexElementFormat.Vector3,  VertexElementMethod.Default, VertexElementUsage.Position, 0),
       new VertexElement( 0, sizeof(float)*3, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Normal, 0),
       new VertexElement( 0, sizeof(float)*6, VertexElementFormat.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0),
       new VertexElement( 0, sizeof(float)*8, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.Color, 0)
         };

         public static int SizeInBytes = 12 * sizeof(float);
     }

     

    最後に一つわからないことがあります。
    「Takashi SAKAMOTO」さんのシェーダ側のコードとして、

     if (round (Diffuse.a * 255.0) == 1.0) {
         Output.RGBColor [0] = 1.0 ;
     } else if (round (Diffuse.a * 255.0) == 2.0) {
         Output.RGBColor [1] = 1.0 ;
     }

    がありますが、round()内の Diffuse.aに 255.0 を乗じているのは何故なのでしょうか?
    基本的なことだったらすいません。


    それでは、失礼します。

    2008年9月26日 18:57
  •  sakapon さんからの引用

    現在、頂点バッファに5つの三角形を表すデータを入れてそれをモニタに表示しています。
    ピクセルシェーダで処理中のピクセルが何番目の三角形なのかを、ピクセルシェーダの中にて知る方法はありますか?

     

    実質的には可能です.実際,最適化手法として以前から使われています.複数のマテリアル(複数のテクスチャ)を使用したモデルを,1 回の DrawPrimitive で描画するという最適化手法です.

     

    方法は大きく分けて2つあって,ひとつは頂点バッファレベルで何番目の三角形かの情報を埋め込んでしまう方法です.もうひとつはシェーダレベルで自動化する方法で,XNAの場合はXbox 360専用になりますが,頂点シェーダのindexセマンティクス(とvfetch命令)が利用できます.Windowsでは,Direct3D 10レベルの機能(SV_PrimitiveID等)がXNAで使えないため,基本的には頂点バッファに埋め込むことになるでしょう.

     

     sakapon さんからの引用

    ということで、ピクセルシェーダで処理中のピクセルが何番目の三角形なのかをはっきりと知る方法があるのかを探しています。

     

    というわけで,いずれも直接の方法ではありません.基本的には頂点シェーダからピクセルシェーダに適切な情報を流すことになります.

    ただ,PixelShader ではデータ型の範囲などが制限されていたりして結構癖があります.PixelShader での予想外の誤差やクリップに注意しつつ,必要に応じて PIX for Windows 等でデバッグしてみると良いでしょう.

     

     sakapon さんからの引用

    Alpha値として渡した 0.0 と 1.0 は if文を通ってくれるのですが、2.0 には無反応です。
    といいますか、渡した 2.0 は 1.0 として if文を通ってしまいます。
    1より大きな数値は 1 となってしまう状況です。

     

    これについてもドキュメントに書かれています.(Input Color Register)

     

    Remarks

    Color registers are read-only registers. Each register contains four-component RGBA values iterated from input vertices. They have lower precision than most registers, guaranteed to have 8 bits of unsigned data in the range (0, +1). You cannot use more than one in a single instruction.

     

    とあるように,PixelShaderのカラーレジスタを使うと受け渡し時にクリップされてしまい,かつ8bit精度しか保証されておりません.データの受け渡しであれば,Texture Coordinate Register を使ったほうが良いでしょう.

     

     

    2008年9月27日 3:52
  • NyaRuRu さん、詳細で丁寧なご回答ありがとうございました。

     

     NyaRuRu さんからの引用

    データの受け渡しであれば,Texture Coordinate Register を使ったほうが良いでしょう.

     

    早速、下記の情報なども参考にして試してみました。

     

    ドキュメント  VertexElementUsage 列挙型 のTextureCoordinate

    ドキュメント VertexElement.UsageIndex プロパティ

    書籍 「Microsoft XNA UNLEASHED グラフィックスとゲーム開発」の372ページ(このページには特定の用途タイプを持たない情報を保存する際の説明がありました)

     

    その結果、「ピクセルシェーダにて処理中のピクセルが何番目の三角形なのか」を判断することが出来るようになりました。

    個人的な感想として、今回の疑問をクリアするための鍵は

    (1) ピクセルシェーダに渡された数値を if文にて判断する際に使った round関数(絶対にこれを使わなければいけない)。

    (2) シェーダに数値を送る際は、頂点の要素として TextureCoordinateを使い、 用途のインデックスを1にする。

    でした。

    ---------------------------------------------------------------

    Takashi SAKAMOTO さん、

    NyaRuRu さん、

    本当にありがとうございました。

     

    これにて失礼します。

    では。

    2008年9月27日 7:44