目次
LEVEL 0:点| LEVEL 2:光源一個
LEVEL 1:ポリゴン1個
3次元空間上の点を描画できたらポリゴンを描くのは簡単です。ポリゴンの頂点をそれぞれスクリーンに投影し、その中身を塗りつぶせばいいだけです。
#この記事では三角ポリゴンのみを扱うことにします。
#それ以外のポリゴンでは同一平面上に頂点がなかったり、
#描画に余計な手間がかかるので。
typedef struct{
Vertex vertex[3];
}Polygon;
typedef struct{
int x;
int y;
}Vertex2;
typedef struct{
Vertex2 vertex[3];
}Triangle;
void DrawPolygon(Polygon* poly, int offsetX, int offsetY){
int i;
Triangle tri;
for(i = 0; i < 3; i++){
if(poly->vertex[i].z <= kScreenDistance)
return;
tri.vertex[i].x = ((vertex.x *kScreenDistance) /vertex.z) +offsetX;
tri.vertex[i].y = ((vertex.y *kScreenDistance) /vertex.z) +offsetY;
}
FillTriangle(&tri); // 三角形を塗りつぶす関数
}
#上のソースでは説明を簡単にするため、
#拡張がしにくいデータ構造になっていますが、ご勘弁を。
#あと、for文は展開したほうが速いかも。
##プロセッサによるけど。PPCの様にプロセッサキャッシュに
##頼ってるプロセッサでは展開しないほうが
##(サイズが小さいほうが)速いかもしれない
で、上のソースの肝はFillTriangle関数です。OSでこういった関数が用意されてればそれを使ってもいいのですが、細かい制御がきかないとか高性能すぎるとかいろいろあるので、自分で実装することにしましょう。
LEVEL 1-2:三角形の塗りつぶし
三角形の塗りつぶしには「最大最小法」と呼ばれる手法を使うことにします。これは、下図のようにポリゴンを描画する範囲のx座標の最大値と最小値を求め、その間を水平のラインで結ぶというものです。
三角形の塗りつぶし
#define kScreenWidth 640
#define kScreenHeight 480
short gMinMaxTable[kScreenHight *2];
void FillTriangle(Triangle* tri, UInt8 indexColor){
SInt16 yArray[3];
SInt16 maxY, minY;
int y;
yArray[0] = tri->pt[0].y;
yArray[1] = tri->pt[1].y;
yArray[2] = tri->pt[2].y;
maxY = MaxSInt16(yArray, 3); // 三角形のy座標の最大値を求める
minY = MinSInt16(yArray, 3); // 三角形のy座標の最小値を求める
// テーブルの初期化
for(y = minY; y <= maxY; y++){
gMinMaxTable[y*2] = 0x7fff; // 最小値として32767を設定
gMinMaxTable[y*2 +1] = 0x8000; // 最大値として-32768を設定
}
// Min-Maxテーブルを設定
SetMinMax(tri->pt[0], tri->pt[1]);
SetMinMax(tri->pt[1], tri->pt[2]);
SetMinMax(tri->pt[2], tri->pt[0]);
// 水平ラインの描画
minY = (minY > 0)? minY: 0;
maxY = (maxY < kScreenHeight)? maxY: kScreenHeight -1;
for(y = minY; y <= maxY; y++){
if(gMinMaxTable[y*2] < 0)
gMinMaxTable[y*2] = 0;
if(gMinMaxTable[y*2 +1] >= kScreenWidth)
gMinMaxTable[y*2 +1] = kScreenWidth -1;
if(gMinMaxTable[y*2 +1] < gMinMaxTable[y*2])
continue;
Line(gMinMaxTable[y*2], y, gMinMaxTable[y*2 +1], y);
}
}
さて、問題はどうやってx座標の最大値と最小値を求めるかですが、これにはブレゼンハムのアルゴリズムといわれる直線描画アルゴリズムを使用します。ここで真面目な解説書ならブレゼンハムのアルゴリズムを詳しく解説するところですが(というより、まず最初にブレゼンハムのアルゴリズムを解説することから始める)、この記事はいい加減なので、出来合いのプログラムに手を加えることにします。元となるプログラムは名著、『C言語によるアルゴリズム大事典第2版』の”グラフィックス”の項にあるプログラムです。
#ブレゼンハムのアルゴリズムは簡単で、しかも応用がきくので
#是非身に付けてください。CG関係の入門書には大抵解説が載っています。
void SetMinMax(Vertex2 pt1, Vertex2 pt2){
int dx, dy, s, step;
dx = pt1.h -pt2.x; if(dx < 0) dx = -dx;
dy = pt1.y -pt2.y; if(dy < 0) dy = -dy;
if(dx > dy){
if(pt1.x > pt2.x){
step = (pt1.y > pt2.y)? 1: -1;
s = pt1.x;
pt1.x = pt2.x;
pt2.x = s;
pt1.y = pt2.y;
}else{
step = (pt1.y < pt2.y)? 1: -1;
}
if(pt1.y > 0 && pt1.y < kMaxHeight){
if(pt1.x < gMinMaxTable[pt1.y*2])
gMinMaxTable[pt1.y*2] = pt1.x;
if(pt1.x > gMinMaxTable[pt1.y*2 +1])
gMinMaxTable[pt1.y*2 +1] = pt1.x;
}
s = dx >>1;
while(++pt1.x <= pt2.x){
if((s -= dy) < 0){
s += dx;
pt1.y += step;
}
if(pt1.y > 0 && pt1.y < kMaxHeight){
if(pt1.x < gMinMaxTable[pt1.y*2])
gMinMaxTable[pt1.y*2] = pt1.x;
if(pt1.x > gMinMaxTable[pt1.y*2 +1])
gMinMaxTable[pt1.y*2 +1] = pt1.x;
}
}
}else{
if(pt1.y > pt2.y){
step = (pt1.x > pt2.x)? 1: -1;
s = pt1.y;
pt1.y = pt2.y;
pt2.y = s;
pt1.x = pt2.x;
}else{
step = (pt1.x < pt2.x)? 1: -1;
}
if(pt1.y > 0 && pt1.y < kMaxHeight){
if(pt1.x < gMinMaxTable[pt1.y*2])
gMinMaxTable[pt1.y*2] = pt1.x;
if(pt1.x > gMinMaxTable[pt1.y*2 +1])
gMinMaxTable[pt1.y*2 +1] = pt1.x;
}
s = dy >>1;
while(++pt1.y <= pt2.y){
if((s -= dx) < 0){
s += dy;
pt1.x += step;
}
if(pt1.y > 0 && pt1.y < kMaxHeight){
if(pt1.x < gMinMaxTable[pt1.y*2])
gMinMaxTable[pt1.y*2] = pt1.x;
if(pt1.x > gMinMaxTable[pt1.y*2 +1])
gMinMaxTable[pt1.y*2 +1] = pt1.x;
}
}
}
}
これで、ポリゴン一個を描画することができました。
目次
LEVEL 0:点| LEVEL 2:光源一個