SHARP EM・ONE の 3D 機能

スマートフォンも高機能なものがあちこちから出てきました。
その中でも気になったのがこれ、イー・モバイルの SHARP EM・ONE です。

http://plusd.itmedia.co.jp/mobile/articles/0702/19/news037.html

ワイド VGA の 800x480dot とか、18.9mm の薄さとか、目をひくスペックが
いろいろ書かれてますが
それよりも一番気になったのがここ

NVIDIA GoForce 5500 搭載!

NVIDIA GoForce 5500

・2.6M Triangles/sec (260万ポリゴン)
・200M pixels/sec
・Geometry Processor
・Programmable Pixel Shaders
・Multi-Texturing
・Early-Z
・OpenGL ES (with NVIDIA Extensions)

なんと PixelShader まで付いてる!
やっぱりスマートフォンも GPU 搭載の時代です。

WindowsMobile なのでとりあえず Direct3DMobile (D3D8相当Fixedのみ) が
使えると思うし、解像度が高いので少々 Fill が厳しいけど、
まじめに作ったら携帯ゲーム機代わりになりそうです。

あと D3DM も Shader に対応してほしいですね。
WindowsMobile で OpenGL ES は使えるのかな。
CPU 側にも FPU か FP 対応 SIMD 命令が欲しくなります。

GeForce8800 (G80) CUDA

GPU 用の汎用 Computing のための SDK である CUDA に動きがあったようです。
いろいろ新しい情報が公開されています。
CUDA for GPU Computing

NVIDIA CUDA Homepage

年々強化されている GPU 演算能力が、プログラマブル機能の進化によって
グラフィック用途向け以外でも活用できるようになって来ました。
ただ Direct3D や OpenGL などの Graphic 向けに特化されている API セットでは
汎用化が進んでいるとはいえやはり制限はついてしまいます。

もっとプログラマが自由に使えて、それでいて強大な演算能力が欲しい。
そういう要望にこたえたのが、CPU からのアプローチでいえば Cell であって
GPU 側からの回答のひとつがこの G80 + CUDA です。
CPU も GPU もこの両者は近づきつつあるのかもしれません。
(本当にくっついちゃったのが AMD + ATI です)

対応しているのは WindowsXP と RedHat系 Linux。
Vista が含まれていないのが少々残念。
Linux がしっかりサポートされているのは最近の NVIDIA の傾向です。
この流れで PS3 Linux でも動いたりしたら面白いのですが
対応 GPU は GeForce8800 (G80) 系だけです。

D3D10 GeometryShaderでsprite(2)

D3D10 GeometryShaderでspriteで書いた
1頂点を4点のポリゴンに展開する GeometryShader の例です。
VS_OUTPUT の Size には、xy に texcoord のサイズが、zw に右下の頂点座標が
入ります。この zw の計算は VertexShader で行っています。

VS_OUTPUT は VertexShader の出力、GS_OUTPUT はそのまま PixelShader の入力
になります。

struct VS_OUTPUT {
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD;
float4 Size : SIZE; // .xy=uvsize .zw=right/bottom
float4 Color : COLOR;
};

struct GS_OUTPUT {
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD;
float4 Color : COLOR;
};

[maxvertexcount(6)]
void GS_Main( point VS_OUTPUT In[1], inout TriangleStream gsstream )
{
GS_OUTPUT Out;

// 左上三角
Out.Pos= In[0].Pos;
Out.Tex= In[0].Tex;
Out.Color= In[0].Color;
gsstream.Append( Out );

Out.Pos= In[0].Pos; Out.Pos.x= In[0].Size.z;
Out.Tex= In[0].Tex; Out.Tex.x+= In[0].Size.x;
Out.Color= In[0].Color;
gsstream.Append( Out );

Out.Pos= In[0].Pos; Out.Pos.y= In[0].Size.w;
Out.Tex= In[0].Tex; Out.Tex.y+= In[0].Size.y;
Out.Color= In[0].Color;
gsstream.Append( Out );

gsstream.RestartStrip();

// 右下三角
Out.Pos= In[0].Pos; Out.Pos.y= In[0].Size.w;
Out.Tex= In[0].Tex; Out.Tex.y+= In[0].Size.y;
Out.Color= In[0].Color;
gsstream.Append( Out );

Out.Pos= In[0].Pos; Out.Pos.x= In[0].Size.z;
Out.Tex= In[0].Tex; Out.Tex.x+= In[0].Size.x;
Out.Color= In[0].Color;
gsstream.Append( Out );

Out.Pos= In[0].Pos; Out.Pos.xy= In[0].Size.zw;
Out.Tex= In[0].Tex; Out.Tex.xy+= In[0].Size.xy;
Out.Color= In[0].Color;
gsstream.Append( Out );

gsstream.RestartStrip();
}

D3D10 GeometryShader で sprite

Direct3D10 では、GeometryShader を使って頂点を増やすことができます。
従来任意の 2D の画像を描画する場合、描画には 4頂点分のデータが必要でした。
例えば x y u v color 等の5要素を 4頂点分使い、TriangleList の 2つの三角
ポリゴンとして描画します。


●頂点
0, 0, 0, 0, 0xffffffff,
32, 0, 1, 0, 0xffffffff,
0, 32, 0, 1, 0xffffffff,
32, 32, 1, 1, 0xffffffff,

●インデックス
0, 1, 2,
2, 1, 3,

そのため CPU 側で4頂点分の計算を行い4頂点分のデータを書き込む必要があります。
D3D10 では、GeometryShader を使うことでもっと簡単に描画できるようになります。

例えば頂点形式を x y w h u v us vs color の 8要素として、PointList を使い

0, 0, 32, 32, 0, 0, 1, 1, 0xfffffff,

となります。PrimitiveType が PointList なのでインデックスは不要です。
これを GeometryShader を使って、4頂点の矩形である 2ポリゴンに変換することが
できます。

昔の PointSprite のような感じです。
違いは uv を自由に指定できて大きさも任意であること。
特徴は 1頂点だけで制御できるので CPU 側の演算が不要でバッファも少なくて
済みます。

こんな風に、GeometryShader は工夫次第でハードウエアでカスタマイズできる
2D 向けの Sprite 機能としても活用できるわけです。

Shader でなんでもできるので、頂点に回転角度を入れて回転機能を持たせる
なんてことももちろん可能でしょう。

D3D10 AlphaTest が無い

そういえば Direct3D10 使っていて AlphaTest が見当たりません。
もともと Direct3D9 の ShaderModel3.0 のときも、
Pixel を捨てるために次の2つの方法がありました。

(1) PixelShader の計算結果の値によって texkill する
(2) AlphaTest を Enable にして Alpha 値で出力する

(2) の方法だと、出力 Alpha を Blend に使ったり RenderTarget に
書き込みたい場合は複雑な計算式になってしまうことがあります。
結局 (1) の方法で (2) も実現できるのである意味重複した機能になっていました。

そういう意味では、D3D10 だと複数の方法から悩まなくて済むのでわかりやすく
なりました。
(それとももしかして AlphaToCoverage が AlphaTest 代わりになるのでしょうか?)

シェーダー側を

float4 PS_Main( PS_INPUT In ) : SV_Target
{
 float4 color= diffuse.Sample( samplestate, In.Tex );

 // AlphaTest
 if( color.w <= 0.001 ){
  clip( -1 );
 }

 return color;
}

のように書き換えて完了です。AlphaTest 相当になりました。
0.001 は AlphaRef に相当します。

clip() は texkill の HLSL 命令です。
値が負のときに pixel を捨てることができます。