3D エンジン

3D エンジンとは、Direct3D や OpenGL のような 3D 描画 API 群の事である。
ここでは、そのソフトウェア版を作成する。
ワールド行列やビュー行列やプロジェクション行列やそれらの掛け合わせ方などは間違っている可能性あり。

サンプル コードのダウンロード
  1. GDI 高速描画環境の構築
  2. 平行投象
  3. 透視投象
  4. ビュー行列
  5. ワールド行列
  6. フラットシェーディング
  7. グーローシェーディング
  8. フォン照明モデル
  9. Z バッファ法
  10. ステンシル バッファ
  11. GDI 高速描画化
  12. テクスチャ表示
  13. マテリアル アルファ ブレンディング
  14. テクスチャ アルファ ブレンディング
  15. フォグ
  16. ピクセル単位のライティング





 GDI 高速描画環境の構築

ペイントソフトの作り方で説明したように、GDI は直接、デバイスコンテキストに描画するよりも、
メモリデバイスコンテキストを介した方が高速に描画できる。その環境を構築する。
#include <windows.h>

#define MYWNDCLASS  "Engine3D_Class"
#define MYWNDTITLE  "Engine3D"
#define WIDTH       200     // 画面の横のサイズ
#define HEIGHT      150     // 画面の縦のサイズ
#define COLOR_BIT   24      // 作業用DIBの色深度
#define WIDTHBYTES(bits)    ((((bits) + 31)>>5)<<2) // 4倍バイト境界幅を求める

HBITMAP ghBmpWork = NULL;
BYTE    *gpBitsWork = NULL;

LRESULT CALLBACK ProcWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Create_Work_Dib();
void M_Create();
void M_Paint(HDC hDC);
void M_Close();

int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hInstPrev,LPSTR lpCmdLine,int nCmdShow)
{
    MSG        msg;
    WNDCLASSEX wc;
    HWND       hWnd;

    ZeroMemory(&wc, sizeof(wc));
    wc.cbSize           = sizeof(WNDCLASSEX); 
    wc.lpfnWndProc      = (WNDPROC)ProcWnd;
    wc.hInstance        = hInst;
    wc.hIcon            = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszMenuName     = NULL;
    wc.lpszClassName    = MYWNDCLASS;
    RegisterClassEx(&wc);
    
    DWORD   dwStyle = WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_CAPTION | WS_VISIBLE;
    RECT    rc = {0, 0, WIDTH, HEIGHT};
    AdjustWindowRectEx(&rc, dwStyle, FALSE, NULL);
    
    hWnd = CreateWindowEx(0, 
                MYWNDCLASS,
                MYWNDTITLE,
                dwStyle,
                CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top,
                NULL,
                NULL,
                hInst,
                NULL);
    
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

LRESULT CALLBACK ProcWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
        M_Create();
        return 0;
    case WM_PAINT:
        {
            HDC         hDC,hDCcp;
            HBITMAP     hBmpPre;
            PAINTSTRUCT ps;
            hDC = BeginPaint(hWnd,&ps);
            Create_Work_Dib();
            if(ghBmpWork){
                hDCcp = CreateCompatibleDC(hDC);
                hBmpPre = (HBITMAP) SelectObject(hDCcp, ghBmpWork);
                HRGN hrgn = CreateRectRgn(0, 0, WIDTH, HEIGHT);
                SelectClipRgn(hDCcp, hrgn);
                DeleteObject(hrgn);
                M_Paint(hDCcp);
                BitBlt(hDC, 0, 0, WIDTH, HEIGHT, hDCcp, 0, 0, SRCCOPY);
                SelectObject(hDCcp, hBmpPre);
                SelectClipRgn(hDCcp, (HRGN) NULL);
                DeleteDC(hDCcp);
                DeleteObject(ghBmpWork);
            }
            EndPaint(hWnd,&ps);
        }
        return 0;
    case WM_CLOSE:
        M_Close();
        DestroyWindow(hWnd);
        PostQuitMessage(0);
        return 0;
    default:
        break;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

// 作業用DIBの作成
void Create_Work_Dib()
{
    BITMAPINFO  bmi;
    
    ZeroMemory(&bmi, sizeof(bmi));
    bmi.bmiHeader.biSize            = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth           = WIDTH;
    bmi.bmiHeader.biHeight          = -HEIGHT;
    bmi.bmiHeader.biPlanes          = 1;
    bmi.bmiHeader.biBitCount        = COLOR_BIT;
    bmi.bmiHeader.biCompression     = BI_RGB;
    
    ghBmpWork = (HBITMAP) CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (VOID **)&gpBitsWork, NULL, 0);
    
    int workSize = WIDTHBYTES(WIDTH * COLOR_BIT) * HEIGHT;
    ZeroMemory(gpBitsWork, workSize);
}// Create_Work_Dib

// 初期化
void M_Create()
{
}// M_Create

// 描画
void M_Paint(HDC hDC)
{
}// M_Paint

// 終了処理
void M_Close()
{
}// M_Close
	
トップへ 前へ 次へ

 平行投象

3D座標を2D座標に変換する事を投象と呼び、平行投象と透視投象がある。
平行投象には、遠近感が出ないが、透視投象には出る。
Direct3Dでは、プロジェクション行列変換が、これに該当し、
平行投象は正射影、透視投象はパースペクティブ射影と呼ばれている。
立方体を平行投象でワイヤーフレーム表示する。
以下のコードを上のコードに置換、または追加する。
参考資料:コンピュータ・グラフィックス J.D.FOLEY/A.VAN DAM著 日本コンピュータ協会 p.282 〜 p.283
typedef struct{
    float   x;
    float   y;
    float   z;
}VEC3,*LPVEC3;

typedef struct{
    VEC3    v[8];       // 頂点座標
    int     f[6][4];    // 各面の頂点番号
}MODEL,*LPMODEL;

MODEL   gmCube;

void Create_Cube();
POINT Ortho(float d_ortho, VEC3 pos, POINT move);
void M_Create();
void M_Paint(HDC hDC);

// 立方体の作成
void Create_Cube()
{
    VEC3    v[8]    =   {{0.5f,0.5f,-0.5f},{0.5f,0.5f,0.5f},{-0.5f,0.5f,0.5f},
                        {-0.5f,0.5f,-0.5f},{0.5f,-0.5f,-0.5f},{0.5f,-0.5f,0.5f},
                        {-0.5f,-0.5f,0.5f},{-0.5f,-0.5f,-0.5f}};
    int     f[6][4] =   {{3,2,1,0},{0,1,5,4},{1,2,6,5},{2,3,7,6},{3,0,4,7},{4,5,6,7}};
    memcpy(gmCube.v, v, sizeof(gmCube.v));
    memcpy(gmCube.f, f, sizeof(gmCube.f));
}// Create_Cube

// 平行投象
POINT Ortho(float d_ortho, VEC3 pos, POINT move)
{
    POINT pt;
    pt.x = (long)( pos.x * d_ortho) + move.x;
    pt.y = (long)(-pos.y * d_ortho) + move.y;
    return pt;
}// Ortho

// 初期化
void M_Create()
{
    Create_Cube();
}// M_Create

// 描画
void M_Paint(HDC hDC)
{
    int         i,j,index;
    POINT       pt[4];
    const float d_ortho = HEIGHT / 2;               // 平行投象での拡大率
    const POINT offset = {WIDTH / 2, HEIGHT / 2};   // 投象後の二次元座標の平行移動幅
    const HPEN  hpen = (HPEN) GetStockObject(WHITE_PEN);
    
    HPEN hpenPre = (HPEN) SelectObject(hDC, hpen);
    for(i=0; i<6; i++){
        for(j=0; j<4; j++){
            index = gmCube.f[i][j];
            pt[j] = Ortho(d_ortho, gmCube.v[index], offset);

        }
        Polyline(hDC, pt, 4);
    }
    SelectObject(hDC, hpenPre);
}// M_Paint
	
トップへ 前へ 次へ

 透視投象

立方体を透視投象でワイヤーフレーム表示する。
平行投象と違い、遠近感(立体感)が出ている。
透視投象には、視点のZ座標情報が必要となる。
以下のコードを上のコードに置換、または追加する。
透視投象のコードを見ると、視点から離れるほど頂点座標が小さくなるのが分かる。
参考資料:3Dネットワークゲーム プログラミングガイド インフィニティ著 秀和システムp.15 〜 p.16
POINT Perspect(float d_perspect, VEC3 pos, float look_z, POINT move);
void M_Paint(HDC hDC);

// 透視投象
POINT Perspect(float d_perspect, VEC3 pos, float look_z, POINT move)
{
    POINT pt;
    if(pos.z == look_z){
        pt.x = move.x;
        pt.y = move.y;
    }
    else{
        pt.x = (long)( pos.x * d_perspect / (pos.z - look_z)) + move.x;
        pt.y = (long)(-pos.y * d_perspect / (pos.z - look_z)) + move.y;
    }
    return pt;
}// Perspect

// 描画
void M_Paint(HDC hDC)
{
    int         i,j,index;
    POINT       pt[4];
    const float d_perspect = HEIGHT;                // 透視投象での投影するスクリーンと視点の距離
    const float look_z = 2.0f;                      // 視点のZ座標
    const POINT offset = {WIDTH / 2, HEIGHT / 2};   // 投象後の二次元座標の平行移動幅
    const HPEN  hpen = (HPEN) GetStockObject(WHITE_PEN);
    
    HPEN hpenPre = (HPEN) SelectObject(hDC, hpen);
    for(i=0; i<6; i++){
        for(j=0; j<4; j++){
            index = gmCube.f[i][j];
            pt[j] = Perspect(d_perspect, gmCube.v[index], look_z, offset);

        }
        Polyline(hDC, pt, 4);
    }
    SelectObject(hDC, hpenPre);
}// M_Paint
	
トップへ 前へ 次へ

 ビュー行列

視点の位置を変えるには、ビュー行列を設定し、頂点座標に適用する。
以下のコードを上のコードに置換、または追加する。
参考資料:Microsoft DirectX9 ヘルプ
#include <math.h>

typedef struct{
    union{
        struct{
            float   _11, _12, _13, _14;
            float   _21, _22, _23, _24;
            float   _31, _32, _33, _34;
            float   _41, _42, _43, _44;
        };
        float m[4][4];
    };
}MTRX,*LPMTRX;

VEC3 Vec3Sub(VEC3 v1, VEC3 v2);
VEC3 Vec3Normalize(VEC3 vin);
VEC3 Vec3Cross(VEC3 v1, VEC3 v2);
MTRX MtrxView(VEC3 v_look, VEC3 v_lookat, VEC3 v_lookup);
VEC3 MtrxVec3(VEC3 v, MTRX mt);
void M_Paint(HDC hDC);

// ベクトルの引き算
VEC3 Vec3Sub(VEC3 v1, VEC3 v2)
{
    VEC3    vout;
    vout.x = v1.x  - v2.x;
    vout.y = v1.y  - v2.y;
    vout.z = v1.z  - v2.z;
    return vout;
}// Vec3Sub

// 単位ベクトル
VEC3 Vec3Normalize(VEC3 vin)
{
    VEC3    vout;
    float   len = (float) sqrt(vin.x * vin.x + vin.y * vin.y + vin.z * vin.z);
    if(len == 0.0f){
        vout.x = 0.0f;
        vout.y = 0.0f;
        vout.z = 0.0f;
    }
    else{
        vout.x = vin.x / len;
        vout.y = vin.y / len;
        vout.z = vin.z / len;
    }
    return vout;
}// Vec3Normalize

// 外積
VEC3 Vec3Cross(VEC3 v1, VEC3 v2)
{
    VEC3    vout;
    vout.x = v1.y * v2.z - v1.z * v2.y;
    vout.y = v1.z * v2.x - v1.x * v2.z;
    vout.z = v1.x * v2.y - v1.y * v2.x;
    return vout;
}// Vec3Cross

// ビュー行列
MTRX MtrxView(VEC3 v_look, VEC3 v_lookat, VEC3 v_lookup)
{
    MTRX mt;
    VEC3 v_x,v_y,v_z;
    v_z = Vec3Normalize(Vec3Sub(v_lookat, v_look));
    v_x = Vec3Normalize(Vec3Cross(v_lookup, v_z));
    v_y = Vec3Cross(v_z, v_x);
    mt._11 = v_x.x;
    mt._12 = v_y.x;
    mt._13 = v_z.x;
    mt._14 = 0;
    mt._21 = v_x.y;
    mt._22 = v_y.y;
    mt._23 = v_z.y;
    mt._24 = 0;
    mt._31 = v_x.z;
    mt._32 = v_y.z;
    mt._33 = v_z.z;
    mt._34 = 0;
    mt._41 = 0;
    mt._42 = 0;
    mt._43 = 0;
    mt._44 = 1;
    return mt;
}// MtrxView

// 行列を座標に適用
VEC3 MtrxVec3(VEC3 v, MTRX mt)
{
    VEC3 v2;
    v2.x = v.x * mt._11 + v.y * mt._21 + v.z * mt._31 + mt._41;
    v2.y = v.x * mt._12 + v.y * mt._22 + v.z * mt._32 + mt._42;
    v2.z = v.x * mt._13 + v.y * mt._23 + v.z * mt._33 + mt._43;
    return v2;
}// MtrxVec3

// 描画
void M_Paint(HDC hDC)
{
    int         i,j;
    POINT       pt[4];
    const float d_perspect = HEIGHT;                // 透視投象での投影するスクリーンと視点の距離
    const POINT offset = {WIDTH / 2, HEIGHT / 2};   // 投象後の二次元座標の平行移動幅
    const VEC3  pos_look = {1.0f, 0.0f, -2.0f};     // 視点の位置
    const VEC3  pos_lookat = {0, 0, 0};             // 注視点の位置
    const VEC3  v_lookup = {0, 1, 0};               // 視点の上向き
    const HPEN  hpen = (HPEN) GetStockObject(WHITE_PEN);
    
    MTRX mt_view = MtrxView(pos_look, pos_lookat, v_lookup);
    
    HPEN hpenPre = (HPEN) SelectObject(hDC, hpen);
    for(i=0; i<6; i++){
        for(j=0; j<4; j++){
            int  index = gmCube.f[i][j];
            VEC3 v = MtrxVec3(gmCube.v[index], mt_view);
            pt[j] = Perspect(d_perspect, v, pos_look.z, offset);
        }
        Polyline(hDC, pt, 4);
    }
    SelectObject(hDC, hpenPre);
}// M_Paint
	
トップへ 前へ 次へ

 ワールド行列

モデルの回転・移動・拡大縮小は、ワールド行列で行う。
モデルは、ローカル空間(モデル空間)からワールド行列変換によってワールド空間に移行し、
ビュー行列変換によってワールド空間からビュー空間(カメラ空間)に移行し、
プロジェクション行列変換によってビュー空間からスクリーン空間へ移行する。
行列も、ワールド行列×ビュー行列×プロジェクション行列の順に掛け合わせる。
以下のコードを上のコードに置換、または追加する。
Polyline() をPolygon() に書き換えると右図のような面描画になるが、
濃淡がないため、立体感が出ない。
LRESULT CALLBACK ProcWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
MTRX MtrxIdent();
MTRX MtrxRotY(float radian);
MTRX MtrxMult(MTRX mt1, MTRX mt2);
void M_Paint(HDC hDC);

LRESULT CALLBACK ProcWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
        M_Create();
        SetTimer(hWnd, 1, 100, NULL);
        return 0;
    case WM_TIMER:
        InvalidateRect(hWnd, NULL, FALSE);  // 再描画
        break;
    case WM_PAINT:
        {
            HDC         hDC,hDCcp;
            HBITMAP     hBmpPre;
            PAINTSTRUCT ps;
            hDC = BeginPaint(hWnd,&ps);
            Create_Work_Dib();
            if(ghBmpWork){
                hDCcp = CreateCompatibleDC(hDC);
                hBmpPre = (HBITMAP) SelectObject(hDCcp, ghBmpWork);
                HRGN hrgn = CreateRectRgn(0, 0, WIDTH, HEIGHT);
                SelectClipRgn(hDCcp, hrgn);
                DeleteObject(hrgn);
                M_Paint(hDCcp);
                BitBlt(hDC, 0, 0, WIDTH, HEIGHT, hDCcp, 0, 0, SRCCOPY);
                SelectObject(hDCcp, hBmpPre);
                SelectClipRgn(hDCcp, (HRGN) NULL);
                DeleteDC(hDCcp);
                DeleteObject(ghBmpWork);
            }
            EndPaint(hWnd,&ps);
        }
        return 0;
    case WM_CLOSE:
        KillTimer(hWnd, 1);
        M_Close();
        DestroyWindow(hWnd);
        PostQuitMessage(0);
        return 0;
    default:
        break;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

// 単位行列
MTRX MtrxIdent()
{
    MTRX mt = { 1,0,0,0,
                0,1,0,0,
                0,0,1,0,
                0,0,0,1};
    return mt;
}// MtrxIdent

// 行列のY軸回転
MTRX MtrxRotY(float radian)
{
    MTRX mt = MtrxIdent();
    mt._11 = (float)  cos(radian);
    mt._13 = (float) -sin(radian);
    mt._31 = (float)  sin(radian);
    mt._33 = (float)  cos(radian);
    return mt;
}// MtrxRotY

// 行列連結
MTRX MtrxMult(MTRX mt1, MTRX mt2)
{
    MTRX mt;
    mt._11 = mt1._11 * mt2._11 + mt1._12 * mt2._21 + mt1._13 * mt2._31 + mt1._14 * mt2._41;
    mt._12 = mt1._11 * mt2._12 + mt1._12 * mt2._22 + mt1._13 * mt2._32 + mt1._14 * mt2._42;
    mt._13 = mt1._11 * mt2._13 + mt1._12 * mt2._23 + mt1._13 * mt2._33 + mt1._14 * mt2._43;
    mt._14 = mt1._11 * mt2._14 + mt1._12 * mt2._24 + mt1._13 * mt2._34 + mt1._14 * mt2._44;
    mt._21 = mt1._21 * mt2._11 + mt1._22 * mt2._21 + mt1._23 * mt2._31 + mt1._24 * mt2._41;
    mt._22 = mt1._21 * mt2._12 + mt1._22 * mt2._22 + mt1._23 * mt2._32 + mt1._24 * mt2._42;
    mt._23 = mt1._21 * mt2._13 + mt1._22 * mt2._23 + mt1._23 * mt2._33 + mt1._24 * mt2._43;
    mt._24 = mt1._21 * mt2._14 + mt1._22 * mt2._24 + mt1._23 * mt2._34 + mt1._24 * mt2._44;
    mt._31 = mt1._31 * mt2._11 + mt1._32 * mt2._21 + mt1._33 * mt2._31 + mt1._34 * mt2._41;
    mt._32 = mt1._31 * mt2._12 + mt1._32 * mt2._22 + mt1._33 * mt2._32 + mt1._34 * mt2._42;
    mt._33 = mt1._31 * mt2._13 + mt1._32 * mt2._23 + mt1._33 * mt2._33 + mt1._34 * mt2._43;
    mt._34 = mt1._31 * mt2._14 + mt1._32 * mt2._24 + mt1._33 * mt2._34 + mt1._34 * mt2._44;
    mt._41 = mt1._41 * mt2._11 + mt1._42 * mt2._21 + mt1._43 * mt2._31 + mt1._44 * mt2._41;
    mt._42 = mt1._41 * mt2._12 + mt1._42 * mt2._22 + mt1._43 * mt2._32 + mt1._44 * mt2._42;
    mt._43 = mt1._41 * mt2._13 + mt1._42 * mt2._23 + mt1._43 * mt2._33 + mt1._44 * mt2._43;
    mt._44 = mt1._41 * mt2._14 + mt1._42 * mt2._24 + mt1._43 * mt2._34 + mt1._44 * mt2._44;
    return mt;
}// MtrxMult

// 描画
void M_Paint(HDC hDC)
{
    int         i,j;
    POINT       pt[4];
    const float d_perspect = HEIGHT;                // 透視投象での投影するスクリーンと視点の距離
    const POINT offset = {WIDTH / 2, HEIGHT / 2};   // 投象後の二次元座標の平行移動幅
    const VEC3  pos_look = {1.0f, 0.0f, -2.0f};     // 視点の位置
    const VEC3  pos_lookat = {0, 0, 0};             // 注視点の位置
    const VEC3  v_lookup = {0, 1, 0};               // 視点の上向き
    const HPEN  hpen = (HPEN) GetStockObject(WHITE_PEN);
    static int  roll_y = 0;                         // 回転角度
    
    MTRX mt_view  = MtrxView(pos_look, pos_lookat, v_lookup);
    MTRX mt_world = MtrxRotY(roll_y * 3.14f / 180);
    MTRX mt_all   = MtrxMult(mt_world, mt_view);
    
    HPEN hpenPre = (HPEN) SelectObject(hDC, hpen);
    for(i=0; i<6; i++){
        for(j=0; j<4; j++){
            int  index = gmCube.f[i][j];
            VEC3 v = MtrxVec3(gmCube.v[index], mt_all);
            pt[j] = Perspect(d_perspect, v, pos_look.z, offset);
        }
        Polyline(hDC, pt, 4);
    }
    SelectObject(hDC, hpenPre);
    roll_y += 10;
    if(roll_y >= 360)
        roll_y = 0;
}// M_Paint
	
トップへ 前へ 次へ

 フラットシェーディング

モデルに照明を当て、面法線ベクトルと入射光ベクトルの成す角度に応じて、各面に色を付ける。
カリングで片面のみ表示する事で、描画速度を向上させると共に、不要な面の描画を避ける。
カリングでは、視線ベクトルと面法線ベクトルの成す角度によって表示する面が決まる。
透視投象の前後では頂点座標が異なるため、カリング用と色用とでは、面法線ベクトルを別々に算出しなくてはならない。
照明反射色には、Lambert 照明モデルを使用した。
Lambert は、余弦定理の発見者である。
以下のコードを上のコードに置換、または追加する。
参考資料:DirectX 逆引き大全500の極意(Lambertモデル)
typedef struct{
    BYTE    r;
    BYTE    g;
    BYTE    b;
}COLOR,*LPCOLOR;

typedef struct{
    VEC3    v[8];       // 頂点の座標
    int     f[6][4];    // 各面の頂点番号
    VEC3    fn[6];      // 面法線ベクトル
}MODEL,*LPMODEL;

MODEL   gmCube;         // 立方体情報

float Vec3Dot(VEC3 v1, VEC3 v2);
VEC3 Perspect(VEC3 pos, float look_z);
VEC3 Get_FaceNormal(VEC3 *v);
void M_Create();
void M_Paint(HDC hDC);

// 内積
float Vec3Dot(VEC3 v1, VEC3 v2)
{
    return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);
}

// 透視投象
VEC3 Perspect(VEC3 pos, float look_z)
{
    VEC3 pos_dst;
    if(pos.z == look_z){
        pos_dst.x = 0;
        pos_dst.y = 0;
    }
    else{
        pos_dst.x = pos.x / (pos.z - look_z);
        pos_dst.y = pos.y / (pos.z - look_z);
    }
    pos_dst.z = pos.z;
    return pos_dst;
}// Perspect

// 面法線の取得
VEC3 Get_FaceNormal(VEC3 *v)
{
    VEC3    v1,v2;
    
    v1 = Vec3Sub(v[2], v[0]);
    v2 = Vec3Sub(v[3], v[2]);
     return Vec3Normalize(Vec3Cross(v1, v2));
}// Get_FaceNormal

// 初期化
void M_Create()
{
    // 立方体作成
    Create_Cube();
    // 面法線ベクトル算出
    int     i,j;
    VEC3    v[4];
    for(i=0; i<6; i++){
        for(j=0; j<4; j++){
            v[j] = gmCube.v[gmCube.f[i][j]];
        }
        gmCube.fn[i] = Get_FaceNormal(v);
    }
}// M_Create

// 描画
void M_Paint(HDC hDC)
{
    int         i,j;
    VEC3        v_prj[4];
    POINT       pt[4];
    const float d_perspect = HEIGHT;                // 透視投象での投影するスクリーンと視点の距離
    const POINT offset = {WIDTH / 2, HEIGHT / 2};   // 投象後の二次元座標の平行移動幅
    const VEC3  pos_look = {0.0f, 2.0f, -2.0f};     // 視点の位置
    const VEC3  pos_lookat = {0, 0, 0};             // 注視点の位置
    const VEC3  v_lookup = {0, 1, 0};               // 視点の上向き
    VEC3        v_sun = {0.0f,-1.0f,1.0f};          // 入射光のベクトル
    VEC3        v_look;                             // 注視点から視点へのベクトル
    HBRUSH      hbr,hbrPre;
    const float power1      = 0.7f;                 // 反射の強さ(0 〜 1)
    const COLOR material    = {255, 255, 255};      // 面のディフューズ色(各色 0 〜 255)
    const COLOR ambient     = {10, 10, 10};         // 環境光(各色 0 〜 255)
    float       power2;
    UINT        r,g,b;
    COLOR       rgb;
    MTRX        mt_view,mt_world,mt_worldview;
    static int  roll_y = 0;                         // 回転角度
    
    // 行列設定
    mt_world        = MtrxRotY(roll_y * 3.14f / 180);
    mt_view         = MtrxView(pos_look, pos_lookat, v_lookup);
    mt_worldview    = MtrxMult(mt_world, mt_view);
    // 入射光ベクトル
    v_sun.x = - v_sun.x;
    v_sun.y = - v_sun.y;
    v_sun.z = - v_sun.z;
    v_sun   = Vec3Normalize(v_sun);
    // 視線ベクトル
    v_look  = Vec3Normalize(MtrxVec3(Vec3Sub(pos_lookat, pos_look), mt_view));
    
    for(i=0; i<6; i++){
        for(j=0; j<4; j++){
            int  index = gmCube.f[i][j];
            // 頂点のワールド・ビュー行列変換
            VEC3 v = Vec3Normalize(MtrxVec3(gmCube.v[index], mt_worldview));
            // 頂点の透視投象
            v_prj[j] = Perspect(v, pos_look.z);
            // 頂点の拡大と平行移動
            pt[j].x = (long)( v_prj[j].x * d_perspect + offset.x);
            pt[j].y = (long)(-v_prj[j].y * d_perspect + offset.y);
        }
        // 面法線ベクトル計算(カリング用)
        VEC3    fn_cull = Get_FaceNormal(v_prj);
        // 面法線ベクトルのワールド行列変換(色用)
        VEC3    fn_color = Vec3Normalize(MtrxVec3(gmCube.fn[i], mt_world));
        // カリング
        float   dot_cull = Vec3Dot(v_look, fn_cull);
        if(dot_cull <= 0){  // 片面表示
            // Lambert 照明モデルによる拡散反射色設定
            power2 = power1 * Vec3Dot(v_sun, fn_color);
            if(power2 < 0){
                power2 = 0;
            }
            r = (UINT)(material.r * power2 + ambient.r);
            g = (UINT)(material.g * power2 + ambient.g);
            b = (UINT)(material.b * power2 + ambient.b);
            if(r > 255) r = 255;
            if(g > 255) g = 255;
            if(b > 255) b = 255;
            rgb.r = (BYTE) r;
            rgb.g = (BYTE) g;
            rgb.b = (BYTE) b;
            hbr = (HBRUSH) CreateSolidBrush(RGB(rgb.r, rgb.g, rgb.b));
            hbrPre = (HBRUSH) SelectObject(hDC, hbr);
            Polygon(hDC, pt, 4);
            SelectObject(hDC, hbrPre);
            DeleteObject(hbr);
        }
    }
    // 回転角度増
    roll_y += 10;
    if(roll_y >= 360)
        roll_y = 0;
}// M_Paint
	
トップへ 前へ 次へ

 グーローシェーディング

フラットシェーディングは面ごとに色分けしたが、頂点ごとに色分けをして、
各面をそのグラデーションで描画するとグーローシェーディングになる。
グーロー(GOURAUD)は、考案者名である。
グーローシェーディングは、曲面を持つ形状の描画に特に有効である。
頂点色は、頂点法線ベクトルと入射光ベクトルの成す角度によって決まる。
頂点法線ベクトルは、その頂点を含む面の法線ベクトルの和である。
面のグラデーション描画は、GradientFill() で三角形を2つ描画する事で行った。
以下のコードを上のコードに置換、または追加する。
GradientFill() の使用には、msimg32.lib のリンクが必要である。
#pragma comment(lib,"msimg32.lib")

typedef struct{
    VEC3    v[8];       // 頂点座標
    VEC3    n[8];       // 頂点法線ベクトル
    int     f[6][4];    // 各面の頂点番号
    VEC3    fn[6];      // 面法線ベクトル
}MODEL,*LPMODEL;

void Set_Normal();
void M_Create();
void M_Paint(HDC hDC);

// 面・頂点法線ベクトル算出
void Set_Normal()
{
    int     i,j,k;
    VEC3    n_v,v[4];
    
    // 面法線ベクトル算出
    for(i=0; i<6; i++){
        for(j=0; j<4; j++){
            v[j] = gmCube.v[gmCube.f[i][j]];
        }
        gmCube.fn[i] = Get_FaceNormal(v);
    }
    // 頂点法線ベクトル算出
    for(i=0; i<8; i++){
        n_v.x = 0.0f;
        n_v.y = 0.0f;
        n_v.z = 0.0f;
        for(j=0; j<6; j++){
            for(k=0; k<4; k++){
                if(i == gmCube.f[j][k]){
                    n_v.x += gmCube.fn[j].x;
                    n_v.y += gmCube.fn[j].y;
                    n_v.z += gmCube.fn[j].z;
                    break;
                }
            }
        }
        gmCube.n[i] = Vec3Normalize(n_v);
    }
}// Set_Normal

// 初期化
void M_Create()
{
    // 立方体作成
    Create_Cube();
    // 面・頂点法線ベクトル設定
    Set_Normal();
}// M_Create

// 描画
void M_Paint(HDC hDC)
{
    int         i,j;
    VEC3        v_prj[4];
    const float d_perspect = HEIGHT;                // 透視投象での投影するスクリーンと視点の距離
    const POINT offset = {WIDTH / 2, HEIGHT / 2};   // 投象後の二次元座標の平行移動幅
    const VEC3  pos_look = {0.0f, 2.0f, -2.0f};     // 視点の位置
    const VEC3  pos_lookat = {0, 0, 0};             // 注視点の位置
    const VEC3  v_lookup = {0, 1, 0};               // 視点の上向き
    VEC3        v_sun = {0.0f, -1.0f, 1.0f};        // 入射光のベクトル
    VEC3        v_look;                             // 注視点から視点へのベクトル
    const float power1      = 0.7f;                 // 反射の強さ(0 〜 1)
    const COLOR material    = {255, 255, 255};      // 面のディフューズ色(各色 0 〜 255)
    const COLOR ambient     = {10, 10, 10};         // 環境光(各色 0 〜 255)
    float       power2;
    UINT        r,g,b;
    COLOR       rgb;
    MTRX        mt_view,mt_world,mt_worldview;
    static int  roll_y = 0;                         // 回転角度
    TRIVERTEX           vert[4];
    GRADIENT_TRIANGLE   gTri[2];
    
    // 行列設定
    mt_world        = MtrxRotY(roll_y * 3.14f / 180);
    mt_view         = MtrxView(pos_look, pos_lookat, v_lookup);
    mt_worldview    = MtrxMult(mt_world, mt_view);
    // 入射光ベクトル
    v_sun.x = - v_sun.x;
    v_sun.y = - v_sun.y;
    v_sun.z = - v_sun.z;
    v_sun   = Vec3Normalize(v_sun);
    // 視線ベクトル
    v_look  = Vec3Normalize(MtrxVec3(Vec3Sub(pos_lookat, pos_look), mt_view));
    
    for(i=0; i<6; i++){
        for(j=0; j<4; j++){
            int  index = gmCube.f[i][j];
            // 頂点のワールド・ビュー行列変換
            VEC3 v = Vec3Normalize(MtrxVec3(gmCube.v[index], mt_worldview));
            // 頂点の透視投象
            v_prj[j] = Perspect(v, pos_look.z);
        }
        // 面法線ベクトル計算(カリング用)
        VEC3    fn_cull = Get_FaceNormal(v_prj);
        // カリング
        float   dot_cull = Vec3Dot(v_look, fn_cull);
        if(dot_cull <= 0){  // 片面表示
            for(j=0; j<4; j++){
                int  index = gmCube.f[i][j];
                // 頂点法線ベクトルのワールド行列変換
                VEC3    n_color = Vec3Normalize(MtrxVec3(gmCube.n[index], mt_world));
                // Lambert 照明モデルによる拡散反射色設定
                power2 = power1 * Vec3Dot(v_sun, n_color);
                if(power2 < 0){
                    power2 = 0;
                }
                r = (UINT)(material.r * power2 + ambient.r);
                g = (UINT)(material.g * power2 + ambient.g);
                b = (UINT)(material.b * power2 + ambient.b);
                if(r > 255) r = 255;
                if(g > 255) g = 255;
                if(b > 255) b = 255;
                rgb.r = (BYTE) r;
                rgb.g = (BYTE) g;
                rgb.b = (BYTE) b;
                vert[j].Red     = rgb.r << 8;
                vert[j].Green   = rgb.g << 8;
                vert[j].Blue    = rgb.b << 8;
                vert[j].Alpha   = 0x0000;
                vert[j].x       = (long)( v_prj[j].x * d_perspect + offset.x);
                vert[j].y       = (long)(-v_prj[j].y * d_perspect + offset.y);
            }
            gTri[0].Vertex1 = 0;
            gTri[0].Vertex2 = 1;
            gTri[0].Vertex3 = 2;
            gTri[1].Vertex1 = 0;
            gTri[1].Vertex2 = 2;
            gTri[1].Vertex3 = 3;
            GradientFill(hDC, vert, 4, &gTri, 2, GRADIENT_FILL_TRIANGLE);
        }
    }
    // 回転角度増
    roll_y += 10;
    if(roll_y >= 360)
        roll_y = 0;
}// M_Paint
	
トップへ 前へ 次へ

 フォン照明モデル

Lambert の代わりに、Phong を使うと、金属的な質感のある立体描写ができる。
Lambert も Phong も考案者名である。
マテリアルやテクスチャが無い場合は、単に置き換えれば良いが、
ある場合は、ポリゴン内の各ピクセル値は以下の式になる。
ピクセル値=テクスチャ色×ディフューズ色×Lambert 照明色+スペキュラ色×Phong 照明色+アンビエント色
この事から、Lambert は陰影に、Phong は光沢のグラデーションに使う事が分かる。
DirectX では、Lambert をディフューズ反射、Phong をスペキュラ反射と呼んでいる。
上のコードの以下の赤字部分を書き換える。
参考資料
Microsoft DirectX9 ヘルプ(簡易 Phong 照明モデル)
DirectX 逆引き大全500の極意(金属のような質感 Pシェーダー)
// 描画
void M_Paint(HDC hDC)
{
    int         i,j;
    VEC3        v_prj[4];
    const float d_perspect = HEIGHT;                // 透視投象での投影するスクリーンと視点の距離
    const POINT offset = {WIDTH / 2, HEIGHT / 2};   // 投象後の二次元座標の平行移動幅
    const VEC3  pos_look = {0.0f, 2.0f, -2.0f};     // 視点の位置
    const VEC3  pos_lookat = {0, 0, 0};             // 注視点の位置
    const VEC3  v_lookup = {0, 1, 0};               // 視点の上向き
    VEC3        v_sun = {0.0f, -1.0f, 1.0f};        // 入射光のベクトル
    VEC3        v_look;                             // 注視点から視点へのベクトル
    const float power1 = 0.7f;                      // 反射の強さ(0 〜 1)
    const int   specular_power = 3;                 // スペキュラ反射の強さ
    const COLOR material = {255, 255, 255};         // 面のディフューズ色(各色 0 〜 255)
    const COLOR ambient = {10, 10, 10};             // 環境光(各色 0 〜 255)
    float       power2;
    UINT        r,g,b;
    COLOR       rgb;
    MTRX        mt_view,mt_world,mt_worldview;
    static int  roll_y = 0;                         // 回転角度
    TRIVERTEX           vert[4];
    GRADIENT_TRIANGLE   gTri[2];
    
    // 行列設定
    mt_world        = MtrxRotY(roll_y * 3.14f / 180);
    mt_view         = MtrxView(pos_look, pos_lookat, v_lookup);
    mt_worldview    = MtrxMult(mt_world, mt_view);
    // 入射光ベクトル
    v_sun.x = - v_sun.x;
    v_sun.y = - v_sun.y;
    v_sun.z = - v_sun.z;
    v_sun   = Vec3Normalize(v_sun);
    // 視線ベクトル
    v_look  = Vec3Normalize(MtrxVec3(Vec3Sub(pos_lookat, pos_look), mt_view));
    
    for(i=0; i<6; i++){
        for(j=0; j<4; j++){
            int  index = gmCube.f[i][j];
            // 頂点のワールド・ビュー行列変換
            VEC3 v = Vec3Normalize(MtrxVec3(gmCube.v[index], mt_worldview));
            // 頂点の透視投象
            v_prj[j] = Perspect(v, pos_look.z);
        }
        // 面法線ベクトル計算(カリング用)
        VEC3    fn_cull = Get_FaceNormal(v_prj);
        // カリング
        float   dot_cull = Vec3Dot(v_look, fn_cull);
        if(dot_cull <= 0){  // 片面表示
            for(j=0; j<4; j++){
                int  index = gmCube.f[i][j];
                // 頂点法線ベクトルのワールド行列変換
                VEC3    n_color = Vec3Normalize(MtrxVec3(gmCube.n[index], mt_world));
                // 簡易 Phong 照明モデルによるスペキュラ反射色設定
                power2 = power1 * (float) pow(Vec3Dot(v_sun, n_color), (int) specular_power);
                if(power2 < 0){
                    power2 = 0;
                }
                r = (UINT)(material.r * power2 + ambient.r);
                g = (UINT)(material.g * power2 + ambient.g);
                b = (UINT)(material.b * power2 + ambient.b);
                if(r > 255) r = 255;
                if(g > 255) g = 255;
                if(b > 255) b = 255;
                rgb.r = (BYTE) r;
                rgb.g = (BYTE) g;
                rgb.b = (BYTE) b;
                vert[j].Red     = rgb.r << 8;
                vert[j].Green   = rgb.g << 8;
                vert[j].Blue    = rgb.b << 8;
                vert[j].Alpha   = 0x0000;
                vert[j].x       = (long)( v_prj[j].x * d_perspect + offset.x);
                vert[j].y       = (long)(-v_prj[j].y * d_perspect + offset.y);
            }
            gTri[0].Vertex1 = 0;
            gTri[0].Vertex2 = 1;
            gTri[0].Vertex3 = 2;
            gTri[1].Vertex1 = 0;
            gTri[1].Vertex2 = 2;
            gTri[1].Vertex3 = 3;
            GradientFill(hDC, vert, 4, &gTri, 2, GRADIENT_FILL_TRIANGLE);
        }
    }
    // 回転角度増
    roll_y += 10;
    if(roll_y >= 360)
        roll_y = 0;
}// M_Paint
	
トップへ 前へ 次へ

 Z バッファ法

Z バッファ法を使う事で、Z 値に応じたポリゴン描画ができる。
Z バッファ法は、先ず、ウィンドウサイズのZ バッファを作成し、
描画前にZ座標の最奥値で初期化する。
ピクセルを描画する際には、そのピクセルのZ座標とZ バッファの該当座標のZ値と比較し、
小さければ描画し、大きければ描画しない。
描画した後は、Zバッファの該当座標の値をそのピクセルのZ座標で更新する。
Z バッファ法は、凹立体や複数のオブジェクトを描画する際に必要になる。
これを使うには、GradientFill() を自作しなくてはならない。
多角形塗りを利用する。
X,Y座標のクリッピングは、頂点座標が画面内に収まるようにしただけの簡単な手法を用いたが、頂点数が少ないポリゴンでは、正確な描画ができないため、あまり望ましくない。
このビュー・プロジェクション行列では、頂点座標をX軸方向に平行移動すると、カリングが上手くできない。
何か間違えているらしい。カリングを外すと正しく動作する。
以下のコードを上のコードに置換、または追加する。
参考資料:
DirectX8.0 3Dアクションゲーム・プログラミング 登大遊著 工学社(Z バッファ法)
コンピュータ・グラフィックス J.D.FOLEY/A.VAN DAM著 日本コンピュータ協会
(明度補完)p.594
(多角形のスキャン変換)p.466〜p.470
typedef struct{
    BYTE    r;
    BYTE    g;
    BYTE    b;
}RGBB,*LPRGBB;

typedef struct{
    int a;
    int r;
    int g;
    int b;
}ARGBN,*LPARGBN;

typedef struct{
    POINT       pos;    // 頂点座標
    ARGBN       c;      // 頂点照明色
    float       z;      // 頂点Z値
}POLYPAINT,*LPPOLYPAINT;

typedef struct{
    int         y_min;  // 辺の最小Y座標
    int         y_max;  // 辺の最大Y座標
    int         x_min;  // 辺の最小Y座標のX座標
    int         x;      // スキャンラインと辺の交点のX座標
    double      slant;  // 辺の傾きの逆数の整数部
    double      slant_sum;  // slantの和
    ARGBN       c_min;  // 辺の最小Y座標の照明色
    ARGBN       c_max;  // 辺の最大Y座標の照明色
    ARGBN       color;  // 照明色
    float       z_min;  // 辺の最小Y座標のZ値
    float       z_max;  // 辺の最大Y座標のZ値
    float       z;      // Z値
    int         next;   // 次のデータ番号
}EDGE,*LPEDGE;

typedef struct{
    int     index;      // 最初のデータ番号
    int     last;       // 最後のデータ番号
}ET,*LPET;

typedef struct{
    float   f1;
    float   f2;
}RATE,*LPRATE;

float       gZBuf[HEIGHT][WIDTH];   // Zバッファ
float       gZFar = 1;              // 再奥Z値
MTRX        gMtProj,gMtView,gMtWorld,gMtWorldView,gMtAll;
VEC3        gPosLook,gPosLookAt;
bool        gFlagPerspect = false;  // false:平行投象、true:射影投象

MTRX MtrxTranslate(float x, float y, float z);
MTRX MtrxScal(float x, float y, float z);
MTRX MtrxPerspect(float zn, float zf, float fov_y, float aspect);
MTRX MtrxOrtho(float zn, float zf, float w, float h);
void Clear_ZBuf();
void Draw_Cube(HDC hDC, VEC3 v_sun);
void Paint_Triangle(HDC hDC, LPPOLYPAINT pp);
void Set_Data1(int idx_min, int idx_max, int &data_ct, LPEDGE data, LPPOLYPAINT pply, LPET et);
void Set_Data2(int idx_min, int idx_max, int &data_ct, LPEDGE data, LPPOLYPAINT pply, LPET et);
void Set_ValRate(LPRATE rate, int idx, int idx_min, int idx_max);
void Set_Color(LPARGBN color, LPRATE rate, ARGBN c_min, ARGBN c_max);
void Set_Z(float *z, LPRATE rate, float z_min, float z_max);
int Round(double val);
void M_Paint(HDC hDC);

// 行列の移動
MTRX MtrxTranslate(float x, float y, float z)
{
    MTRX mt = MtrxIdent();
    mt._41 = x;
    mt._42 = y;
    mt._43 = z;
    return mt;
}// MtrxTranslate

// 行列のスケーリング
MTRX MtrxScal(float x, float y, float z)
{
    MTRX mt = MtrxIdent();
    mt._11 = x;
    mt._22 = y;
    mt._33 = z;
    return mt;
}// MtrxScal

// 射影投象行列
MTRX MtrxPerspect(float zn, float zf, float fov_y, float aspect)
{
    float   c, s, Q;
    
    gFlagPerspect = true;
    
    if(zf == 0){
        zf = 0.001f;
    }
    gZFar = zf;
    
    c = (float) cos(fov_y * 0.5);
    s = (float) sin(aspect * 0.5);
    if(zn != zf){
        Q = s / (1.0f - zn / zf);
    }
    else{
        Q = 1.0f;
    }
    
    MTRX mt;
    ZeroMemory(&mt, sizeof(mt));

    mt._11 = c;
    mt._22 = c;
    mt._33 = Q;
    mt._43 = -Q * zn;
    mt._34 = s;
    
    return mt;
}// MtrxPerspect

// 平行投象行列
MTRX MtrxOrtho(float zn, float zf, float w, float h)
{
    float   sub;
    
    gFlagPerspect = false;
    
    gZFar = zf;
    
    if(zn != zf)
        sub = zf - zn;
    else
        sub = 1;
    if(w == 0)
        w = 1;
    if(h == 0)
        h = 1;
    
    MTRX mt;
    ZeroMemory(&mt, sizeof(mt));

    mt._11 = 2.0f / w;
    mt._22 = 2.0f / h;
    mt._33 = 1.0f / sub;
    mt._43 = - zn / sub;
    mt._34 = 1;
    
    return mt;
}// MtrxOrtho

// Zバッファ初期化
void Clear_ZBuf()
{
    int i,j;
    
    for(i=0; i<HEIGHT; i++){
        for(j=0; j<WIDTH; j++){
            gZBuf[i][j] = gZFar;
        }
    }
}// Clear_ZBuf

// 立方体描画
void Draw_Cube(HDC hDC, VEC3 v_sun)
{
    int         i,j;
    VEC3        v_wv[4],v_all[4];
    VEC3        v_look;                             // 注視点から視点へのベクトル
    const int   d_perspect = HEIGHT;                // 透視投象での投影するスクリーンと視点の距離
    const POINT offset = {WIDTH / 2, HEIGHT / 2};   // 投象後の二次元座標の平行移動幅
    const float power1 = 0.7f;                      // 反射の強さ(0 〜 1)
    const int   specular_power = 3;                 // スペキュラ反射の強さ
    const RGBB  material = {255, 255, 255};         // 面のディフューズ色(各色 0 〜 255)
    const RGBB  ambient = {10, 10, 10};             // 環境光(各色 0 〜 255)
    float       power2;
    UINT        r,g,b;
    POLYPAINT   ply1[4];                            // 4頂点ポリゴン色塗り情報
    POLYPAINT   ply2[2][3];                         // 3頂点ポリゴン色塗り情報
    
    gMtWorldView    = MtrxMult(gMtWorld, gMtView);
    gMtAll          = MtrxMult(gMtWorldView, gMtProj);
    
    // 視線ベクトル
    VEC3    posLookAt = MtrxVec3(gPosLookAt, gMtWorld);
    v_look  = Vec3Normalize(MtrxVec3(Vec3Sub(posLookAt, gPosLook), gMtView));
    
    for(i=0; i<6; i++){
        for(j=0; j<4; j++){
            int  index = gmCube.f[i][j];
            // 頂点のワールド・ビュー行列変換
            v_wv[j] = Vec3Normalize(MtrxVec3(gmCube.v[index], gMtWorldView));
        }
        // 面法線ベクトル計算(カリング用)
        VEC3    fn_cull = Get_FaceNormal(v_wv);
        // カリング
        float   dot_cull = Vec3Dot(v_look, fn_cull);
        if(dot_cull < 0){   // 片面表示
            for(j=0; j<4; j++){
                int  index = gmCube.f[i][j];
                // 頂点のワールド・ビュー・プロジェクション行列変換
                v_all[j] = MtrxVec3(gmCube.v[index], gMtAll);
                // 頂点法線ベクトルのワールド行列変換
                VEC3    n_color;
                n_color = MtrxVec3(gmCube.n[index], gMtWorld);
                n_color = Vec3Normalize(n_color);
                // 簡易 Phong 照明モデルによるスペキュラ反射色設定
                power2 = power1 * (float) pow(Vec3Dot(v_sun, n_color), (int) specular_power);
                if(power2 < 0){
                    power2 = 0;
                }
                r = (UINT)(material.r * power2 + ambient.r);
                g = (UINT)(material.g * power2 + ambient.g);
                b = (UINT)(material.b * power2 + ambient.b);
                if(r > 255) r = 255;
                if(g > 255) g = 255;
                if(b > 255) b = 255;
                float size_rate = d_perspect;
                if(gFlagPerspect)
                    size_rate /= (v_all[j].z + gPosLookAt.z - gPosLook.z);
                ply1[j].pos.x   = (long)( v_all[j].x * size_rate + offset.x);
                ply1[j].pos.y   = (long)(-v_all[j].y * size_rate + offset.y);
                ply1[j].z       = v_all[j].z;
                ply1[j].c.a = 0;
                ply1[j].c.r = r;
                ply1[j].c.g = g;
                ply1[j].c.b = b;
            }
            ply2[0][0] = ply1[0];
            ply2[0][1] = ply1[1];
            ply2[0][2] = ply1[2];
            ply2[1][0] = ply1[0];
            ply2[1][1] = ply1[2];
            ply2[1][2] = ply1[3];
            Paint_Triangle(hDC, ply2[0]);
            Paint_Triangle(hDC, ply2[1]);
        }
    }
}// Draw_Cube

// 三角形塗り
// pply     : 三角形の頂点と色のリスト
void Paint_Triangle(HDC hDC, LPPOLYPAINT pply)
{
    EDGE        data[3];    // 頂点リスト
    EDGE        aet[2];     // アクティブ辺テーブル(AET)
    ET          et[HEIGHT]; // 辺テーブル(ET)
    int         i,j,y_min,y_max,prev,next,next2,data_ct,aet_ct,y_next;
    float       z = 0;      // Z 値
    ARGBN       argbLight;
    RATE        rate;
    
    // Y座標のクリッピング
    for(i=0; i<3; i++){
        if(pply[i].pos.y < 0)
            pply[i].pos.y = 0;
        if(pply[i].pos.y >= HEIGHT)
            pply[i].pos.y = HEIGHT - 1;
    }
    // Y座標の最大値と最小値を求める
    y_min = pply[0].pos.y;
    y_max = pply[0].pos.y;
    for(i=1; i<3; i++){
        if(pply[i].pos.y < y_min){
            y_min = pply[i].pos.y;
        }
        else if(pply[i].pos.y > y_max)
            y_max = pply[i].pos.y;
    }
    // 頂点リスト初期化
    for(i=0; i<3; i++){
        data[i].next = -1;
    }
    // ET 初期化
    for(i=y_min; i<=y_max; i++){
        et[i].index = -1;
        et[i].last = -1;
    }
    data_ct = 0;    // data の要素数
    for(i=0; i<3; i++){
        prev = i - 1;
        if(prev < 0){
            prev = 2;
        }
        next = i + 1;
        if(next >= 3){
            next = 0;
        }
        if(pply[i].pos.y < pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data2(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data2(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y == pply[prev].pos.y){
            Set_Data1(i, i, data_ct, data, pply, et);
        }
    }
    // スキャン・ライン・アルゴリズムによる塗り潰し
    aet_ct = 0; // AET の要素数
    for(i=y_min; i<=y_max; i++){
        // スキャンライン上の頂点を検索
        if(et[i].index != -1){
            aet[aet_ct ++] = data[et[i].index];
            next2 = data[et[i].index].next;
            if(next2 != -1){
                aet[aet_ct ++] = data[next2];
                next2 = data[next2].next;
            }
        }
        // AET を x について昇順にソート
        if(aet[0].x > aet[1].x){
            EDGE e = aet[0];
            aet[0] = aet[1];
            aet[1] = e;
        }
        // X座標のクリッピング
        if(aet[0].x < 0){
            aet[0].x = 0;
        }
        if(aet[1].x >= WIDTH){
            aet[1].x = WIDTH - 1;
        }
        // スキャンラインの描画
        for(j=aet[0].x;j<aet[1].x;j++){
            Set_ValRate(&rate, j, aet[0].x, aet[1].x);
            Set_Z(&z, &rate, aet[0].z, aet[1].z);
            if(z < gZBuf[i][j]){    // ピクセルごとの Z バッファ判定
                gZBuf[i][j] = z;
                // 照明色
                Set_Color(&argbLight, &rate, aet[0].color, aet[1].color);
                BYTE r = (BYTE) argbLight.r;
                BYTE g = (BYTE) argbLight.g;
                BYTE b = (BYTE) argbLight.b;
                // ピクセル値設定
                SetPixel(hDC, j ,i, RGB(r,g,b));
            }
        }
        // i == y_max の要素を AET から削除
        if(i == aet[1].y_max){
            aet_ct --;
        }
        if(i == aet[0].y_max){
            aet[0] = aet[1];
            aet_ct --;
        }
        // AET の各 x を次のスキャンラインでの値に更新
        for(j=0;j<2;j++){
            y_next = i + 1;
            aet[j].x = Round(aet[j].x_min + aet[j].slant_sum);
            aet[j].slant_sum += aet[j].slant;
            Set_ValRate(&rate, y_next, aet[j].y_min, aet[j].y_max);
            Set_Color(&aet[j].color, &rate, aet[j].c_min, aet[j].c_max);
            Set_Z(&aet[j].z, &rate, aet[j].z_min, aet[j].z_max);
        }
    }
}// Paint_Triangle

// 頂点リスト設定1
void Set_Data1(int idx_min, int idx_max, int &data_ct, LPEDGE data, LPPOLYPAINT pply, LPET et)
{
    int et_idx = pply[idx_min].pos.y;
    if(et[et_idx].last == -1)
        et[et_idx].index = data_ct;
    else
        data[et[et_idx].last].next = data_ct;
    et[et_idx].last = data_ct;
    
    data[data_ct].y_min = pply[idx_min].pos.y;
    data[data_ct].y_max = pply[idx_max].pos.y;
    data[data_ct].x_min = pply[idx_min].pos.x;
    data[data_ct].x     = data[data_ct].x_min;
    data[data_ct].slant = (double)(pply[idx_max].pos.x - pply[idx_min].pos.x) / (pply[idx_max].pos.y - pply[idx_min].pos.y);
    data[data_ct].slant_sum = data[data_ct].slant;
    data[data_ct].c_min = pply[idx_min].c;
    data[data_ct].c_max = pply[idx_max].c;
    data[data_ct].color = data[data_ct].c_min;
    data[data_ct].z_min = pply[idx_min].z;
    data[data_ct].z_max = pply[idx_max].z;
    data[data_ct].z     = data[data_ct].z_min;
    data_ct ++;
}// Set_Data1

// 頂点リスト設定2
void Set_Data2(int idx_min, int idx_max, int &data_ct, LPEDGE data, LPPOLYPAINT pply, LPET et)
{
    int et_idx = pply[idx_min].pos.y + 1;
    if(et[et_idx].last == -1)
        et[et_idx].index = data_ct;
    else
        data[et[et_idx].last].next = data_ct;
    et[et_idx].last = data_ct;
    
    double  slant = (double)(pply[idx_max].pos.x - pply[idx_min].pos.x) / (pply[idx_max].pos.y - pply[idx_min].pos.y);
    RATE    rate;
    float   z;
    ARGBN   color;
    Set_ValRate(&rate, et_idx, pply[idx_min].pos.y, pply[idx_max].pos.y);
    data[data_ct].y_min = pply[idx_min].pos.y;
    data[data_ct].y_max = pply[idx_max].pos.y;
    data[data_ct].x_min = pply[idx_min].pos.x;
    data[data_ct].x     = Round(data[data_ct].x_min + slant);
    data[data_ct].slant = slant;
    data[data_ct].slant_sum = data[data_ct].slant + slant;
    Set_Color(&color, &rate, pply[idx_min].c, pply[idx_max].c);
    data[data_ct].c_min = pply[idx_min].c;
    data[data_ct].c_max = pply[idx_max].c;
    data[data_ct].color = color;
    Set_Z(&z, &rate, pply[idx_min].z, pply[idx_max].z);
    data[data_ct].z_min = pply[idx_min].z;
    data[data_ct].z_max = pply[idx_max].z;
    data[data_ct].z     = z;
    data_ct ++;
}// Set_Data2

// 比率設定
void Set_ValRate(LPRATE rate, int idx, int idx_min, int idx_max)
{
    if(idx_min != idx_max){
        rate->f1 = (float)(idx - idx_min) / (idx_max - idx_min);
        rate->f2 = 1 - rate->f1;
    }
    else{
        rate->f1 = -1;
    }
}// Set_ValRate

// 色設定
void Set_Color(LPARGBN color, LPRATE rate, ARGBN c_min, ARGBN c_max)
{
    if(rate->f1 != -1){
        color->a = (BYTE)(c_max.a * rate->f1 + c_min.a * rate->f2);
        color->r = (BYTE)(c_max.r * rate->f1 + c_min.r * rate->f2);
        color->g = (BYTE)(c_max.g * rate->f1 + c_min.g * rate->f2);
        color->b = (BYTE)(c_max.b * rate->f1 + c_min.b * rate->f2);
    }
    else{
        *color = c_min;
    }
}// Set_Color

// Z値設定
void Set_Z(float *z, LPRATE rate, float z_min, float z_max)
{
    if(rate->f1 != -1){
        *z = z_max * rate->f1 + z_min * rate->f2;
    }
    else{
        *z = z_min;
    }
}// Set_Z

// 浮動小数点数を四捨五入して整数にする
int Round(double val)
{
    return (int)(val + 0.5f);
}// Round

// 描画
void M_Paint(HDC hDC)
{
    const VEC3  pos_look = {0.0f, 2.0f, -2.0f};     // 視点の位置
    const VEC3  pos_lookat = {0.0f, 0.0f, 0.0f};    // 注視点の位置
    const VEC3  v_lookup = {0, 1, 0};               // 視点の上向き
    VEC3        v_sun = {0.0f, -1.0f, 1.0f};        // 入射光のベクトル
    const RGBB  material = {255, 255, 255};         // 面のディフューズ色(各色 0 〜 255)
    const RGBB  ambient = {10, 10, 10};             // 環境光(各色 0 〜 255)
    MTRX        mtScal,mtRotY,mtTrans,mtWork;
    static int  roll_y = 0;                         // 回転角度
    
    // 行列設定
    gMtView     = MtrxView(pos_look, pos_lookat, v_lookup);
    gMtProj     = MtrxPerspect(0.0f, 10.0f, 3.14f / 4, 1.0f);
//  gMtProj     = MtrxOrtho(0.0f, 10.0f, 5.0f, 5.0f);
    // 入射光ベクトル
    v_sun.x     = - v_sun.x;
    v_sun.y     = - v_sun.y;
    v_sun.z     = - v_sun.z;
    v_sun       = Vec3Normalize(v_sun);
    // Zバッファ初期化
    Clear_ZBuf();
    // オブジェクト描画
    mtScal      = MtrxScal(1.0f, 1.0f, 1.0f);
    mtRotY      = MtrxRotY(roll_y * 3.14f / 180);
    mtTrans     = MtrxTranslate(-0.4f, 0.2f, 0.0f);
    mtWork      = MtrxMult(mtScal, mtRotY);
    gMtWorld    = MtrxMult(mtWork, mtTrans);
    Draw_Cube(hDC, v_sun);
    mtTrans     = MtrxTranslate(0.4f, 0.0f, 0.2f);
    gMtWorld    = MtrxMult(mtWork, mtTrans);
    Draw_Cube(hDC, v_sun);
    // 回転角度増
    roll_y += 10;
    if(roll_y >= 360)
        roll_y = 0;
}// M_Paint
	
トップへ 前へ 次へ

 ステンシル バッファ

ステンシル バッファは、1度書き込んだピクセルには、書き込めないようにする手法である。
その仕組みは、Z バッファ法と、ほぼ同様である。
上のコードに以下の赤字部分を追加する。
参考資料:0からのゲームプログラミング (http://www.plustarnet.com/aspil/Programming/)影 2
bool        gStencilBuf[HEIGHT][WIDTH]; // ステンシル バッファ
bool        gFlagStencilWrite = false;  // ステンシル バッファ有効化フラグ(書き込み用)
bool        gFlagStencilJudge = false;  // ステンシル バッファ有効化フラグ(判定用)

void Clear_Z_StencilBuf();
void Paint_Triangle(HDC hDC, LPPOLYPAINT pp);
void M_Paint(HDC hDC);

// Z・ステンシルバッファ初期化
void Clear_Z_StencilBuf()
{
    int i,j;
    
    for(i=0; i<HEIGHT; i++){
        for(j=0; j<WIDTH; j++){
            gZBuf[i][j] = gZFar;
            gStencilBuf[i][j] = false;
        }
    }
}// Clear_Z_StencilBuf

// 三角形塗り
// pply     : 三角形の頂点と色のリスト
void Paint_Triangle(HDC hDC, LPPOLYPAINT pply)
{
    EDGE        data[3];    // 頂点リスト
    EDGE        aet[2];     // アクティブ辺テーブル(AET)
    ET          et[HEIGHT]; // 辺テーブル(ET)
    int         i,j,y_min,y_max,prev,next,next2,data_ct,aet_ct,y_next;
    float       z = 0;      // Z 値
    ARGBN       argbLight;
    RATE        rate;
    
    // Y座標のクリッピング
    for(i=0; i<3; i++){
        if(pply[i].pos.y < 0)
            pply[i].pos.y = 0;
        if(pply[i].pos.y >= HEIGHT)
            pply[i].pos.y = HEIGHT - 1;
    }
    // Y座標の最大値と最小値を求める
    y_min = pply[0].pos.y;
    y_max = pply[0].pos.y;
    for(i=1; i<3; i++){
        if(pply[i].pos.y < y_min){
            y_min = pply[i].pos.y;
        }
        else if(pply[i].pos.y > y_max)
            y_max = pply[i].pos.y;
    }
    // 頂点リスト初期化
    for(i=0; i<3; i++){
        data[i].next = -1;
    }
    // ET 初期化
    for(i=y_min; i<=y_max; i++){
        et[i].index = -1;
        et[i].last = -1;
    }
    data_ct = 0;    // data の要素数
    for(i=0; i<3; i++){
        prev = i - 1;
        if(prev < 0){
            prev = 2;
        }
        next = i + 1;
        if(next >= 3){
            next = 0;
        }
        if(pply[i].pos.y < pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data2(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data2(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y == pply[prev].pos.y){
            Set_Data1(i, i, data_ct, data, pply, et);
        }
    }
    // スキャン・ライン・アルゴリズムによる塗り潰し
    aet_ct = 0; // AET の要素数
    for(i=y_min; i<=y_max; i++){
        // スキャンライン上の頂点を検索
        if(et[i].index != -1){
            aet[aet_ct ++] = data[et[i].index];
            next2 = data[et[i].index].next;
            if(next2 != -1){
                aet[aet_ct ++] = data[next2];
                next2 = data[next2].next;
            }
        }
        // AET を x について昇順にソート
        if(aet[0].x > aet[1].x){
            EDGE e = aet[0];
            aet[0] = aet[1];
            aet[1] = e;
        }
        // X座標のクリッピング
        if(aet[0].x < 0){
            aet[0].x = 0;
        }
        if(aet[1].x >= WIDTH){
            aet[1].x = WIDTH - 1;
        }
        // スキャンラインの描画
        for(j=aet[0].x;j<aet[1].x;j++){
            Set_ValRate(&rate, j, aet[0].x, aet[1].x);
            Set_Z(&z, &rate, aet[0].z, aet[1].z);
            if(z < gZBuf[i][j] && (!gFlagStencilJudge ||
                gFlagStencilJudge && !gStencilBuf[i][j]))   // ピクセルごとの Z バッファ・ステンシル バッファ判定
            {
                gZBuf[i][j] = z;
                if(gFlagStencilWrite){
                    gStencilBuf[i][j] = true;
                }
                // 照明色
                Set_Color(&argbLight, &rate, aet[0].color, aet[1].color);
                BYTE r = (BYTE) argbLight.r;
                BYTE g = (BYTE) argbLight.g;
                BYTE b = (BYTE) argbLight.b;
                // ピクセル値設定
                SetPixel(hDC, j ,i, RGB(r,g,b));
            }
        }
        // i == y_max の要素を AET から削除
        if(i == aet[1].y_max){
            aet_ct --;
        }
        if(i == aet[0].y_max){
            aet[0] = aet[1];
            aet_ct --;
        }
        // AET の各 x を次のスキャンラインでの値に更新
        for(j=0;j<2;j++){
            y_next = i + 1;
            aet[j].x = Round(aet[j].x_min + aet[j].slant_sum);
            aet[j].slant_sum += aet[j].slant;
            Set_ValRate(&rate, y_next, aet[j].y_min, aet[j].y_max);
            Set_Color(&aet[j].color, &rate, aet[j].c_min, aet[j].c_max);
            Set_Z(&aet[j].z, &rate, aet[j].z_min, aet[j].z_max);
        }
    }
}// Paint_Triangle

// 描画
void M_Paint(HDC hDC)
{
    const VEC3  pos_look = {0.0f, 2.0f, -2.0f};     // 視点の位置
    const VEC3  pos_lookat = {0.0f, 0.0f, 0.0f};    // 注視点の位置
    const VEC3  v_lookup = {0, 1, 0};               // 視点の上向き
    VEC3        v_sun = {0.0f, -1.0f, 1.0f};        // 入射光のベクトル
    const RGBB  material = {255, 255, 255};         // 面のディフューズ色(各色 0 〜 255)
    const RGBB  ambient = {10, 10, 10};             // 環境光(各色 0 〜 255)
    MTRX        mtScal,mtRotY,mtTrans,mtWork;
    static int  roll_y = 0;                         // 回転角度
    
    // 行列設定
    gMtView     = MtrxView(pos_look, pos_lookat, v_lookup);
    gMtProj     = MtrxPerspect(0.0f, 10.0f, 3.14f / 4, 1.0f);
//  gMtProj     = MtrxOrtho(0.0f, 10.0f, 5.0f, 5.0f);
    // 入射光ベクトル
    v_sun.x     = - v_sun.x;
    v_sun.y     = - v_sun.y;
    v_sun.z     = - v_sun.z;
    v_sun       = Vec3Normalize(v_sun);
    // Z・ステンシルバッファ初期化
    Clear_Z_StencilBuf();
    // オブジェクト描画
    mtScal      = MtrxScal(1.0f, 1.0f, 1.0f);
    mtRotY      = MtrxRotY(roll_y * 3.14f / 180);
    mtTrans     = MtrxTranslate(-0.4f, 0.2f, 0.0f);
    mtWork      = MtrxMult(mtScal, mtRotY);
    gMtWorld    = MtrxMult(mtWork, mtTrans);
    gFlagStencilWrite = true;
    gFlagStencilJudge = false;
    Draw_Cube(hDC, v_sun);
    mtTrans     = MtrxTranslate(0.4f, 0.0f, 0.2f);
    gMtWorld    = MtrxMult(mtWork, mtTrans);
    gFlagStencilWrite = false;
    gFlagStencilJudge = true;
    Draw_Cube(hDC, v_sun);
    // 回転角度増
    roll_y += 10;
    if(roll_y >= 360)
        roll_y = 0;
}// M_Paint
	
トップへ 前へ 次へ

 GDI 高速描画化

SetPixel() は、実は描画が遅く、ピクセル値を直接操作した方が速い。
また、
for(j=0; j<hgt; j++){
    for(i=0; i<wdh; i++){
        pbits[j * stride + i * 3 + 0] = b;
        pbits[j * stride + i * 3 + 1] = g;
        pbits[j * stride + i * 3 + 2] = r;
    }
}
とするよりも、
int stride_sum = 0;
for(j=0; j<hgt; j++){
    BYTE pb = pbits + stride_sum;
    for(i=0; i<wdh; i++){
        *pb ++ = b;
        *pb ++ = g;
        *pb ++ = r;
    }
    stride_sum += stride;
}
とした方が描画が速くなる。
乗除よりも加減やシフトの方が演算速度が速いからである。

もう1つの掛け算速度を上げる方法が、テーブル演算である。
例えば、3 * 5 = 15 という計算をする時、予め、table[3][5] = 15
という計算結果を入れた配列を用意しておけば、計算速度の向上が見込める。
例えば、半透明描画は、
BYTE r1 = 255,g1 = 32,b1 = 32,r2 = 0,g2 = 0,b2 = 255;
float alpha1 = 0.5f;
float alpha2 = 1.0f - alpha1;
BYTE r = (BYTE)(r1 * alpha1 + r2 * alpha2);
BYTE g = (BYTE)(g1 * alpha1 + g2 * alpha2);
BYTE b = (BYTE)(b1 * alpha1 + b2 * alpha2);
で計算されるが、予め
BYTE table[256][256];
for(i=0; i<256; i++){
    for(j=0; j<256; j++){
        table[i][j] = i * j / 255;
    }
}
という掛け算テーブルを作っておけば、
BYTE alpha1 = (BYTE)(0.5f * 255);
BYTE alpha2 = 255 - alpha1;
BYTE r = table[r1][alpha1] + table[r2][alpha2];
BYTE g = table[g1][alpha1] + table[g2][alpha2];
BYTE b = table[b1][alpha1] + table[b2][alpha2];
とできるから、掛け算をしなくて済むというわけである。

Z バッファ法のコードに以下の赤字部分を追加する。
参考資料:DirectX Programmers Page (http://www.interq.or.jp/black/minami-m/) GDI 描画ライブラリ
#define COLOR_BYTE  COLOR_BIT / 8   // 作業用DIBの色深度(バイト)
#define MULT1       255     // 掛け算対応表の被乗数の最大値
#define MULT2       255     // 掛け算対応表の乗数の最大値

typedef struct{
    float   f1;
    float   f2;
    int     n1;
    int     n2;
}RATE,*LPRATE;

UINT        gnStride = 0;               // 作業用DIBの4バイト境界幅
BYTE        gMultTbl[MULT1+1][MULT2+1]; // 掛け算の対応表(比率)
D3DXMATRIX  gMtProj,gMtView,gMtWorld,gMtWorldView,gMtAll;

void Create_Work_Dib();
void Paint_Triangle(HDC hDC, LPPOLYPAINT pp);
void Set_ValRate(LPRATE rate, int idx, int idx_min, int idx_max);
void Set_Color(LPARGBN color, LPRATE rate, ARGBN c_min, ARGBN c_max);
void M_Create();
void M_Paint(HDC hDC);

// 作業用DIBの作成
void Create_Work_Dib()
{
    BITMAPINFO  bmi;
    
    ZeroMemory(&bmi, sizeof(bmi));
    bmi.bmiHeader.biSize            = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth           = WIDTH;
    bmi.bmiHeader.biHeight          = -HEIGHT;
    bmi.bmiHeader.biPlanes          = 1;
    bmi.bmiHeader.biBitCount        = COLOR_BIT;
    bmi.bmiHeader.biCompression     = BI_RGB;
    
    ghBmpWork = (HBITMAP) CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (VOID **)&gpBitsWork, NULL, 0);
    
    gnStride = WIDTHBYTES(WIDTH * COLOR_BIT);
    int workSize = gnStride * HEIGHT;
    ZeroMemory(gpBitsWork, workSize);
}// Create_Work_Dib

// 三角形塗り
// pply     : 三角形の頂点と色のリスト
void Paint_Triangle(HDC hDC, LPPOLYPAINT pply)
{
    EDGE        data[3];       // 頂点リスト
    EDGE        aet[2];        // アクティブ辺テーブル(AET)
    ET          et[HEIGHT];    // 辺テーブル(ET)
    int         i,j,y_min,y_max,prev,next,next2,data_ct,aet_ct,y_next;
    UINT        stride_sum;
    float       z = 0;         // Z 値
    ARGBN       argbLight;
    RATE        rate;
    
    // Y座標のクリッピング
    for(i=0; i<3; i++){
        if(pply[i].pos.y < 0)
            pply[i].pos.y = 0;
        if(pply[i].pos.y >= HEIGHT)
            pply[i].pos.y = HEIGHT - 1;
    }
    // Y座標の最大値と最小値を求める
    y_min = pply[0].pos.y;
    y_max = pply[0].pos.y;
    for(i=1; i<3; i++){
        if(pply[i].pos.y < y_min){
            y_min = pply[i].pos.y;
        }
        else if(pply[i].pos.y > y_max)
            y_max = pply[i].pos.y;
    }
    // 頂点リスト初期化
    for(i=0; i<3; i++){
        data[i].next = -1;
    }
    // ET 初期化
    for(i=y_min; i<=y_max; i++){
        et[i].index = -1;
        et[i].last = -1;
    }
    data_ct = 0;    // data の要素数
    for(i=0; i<3; i++){
        prev = i - 1;
        if(prev < 0){
            prev = 2;
        }
        next = i + 1;
        if(next >= 3){
            next = 0;
        }
        if(pply[i].pos.y < pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data2(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data2(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y == pply[prev].pos.y){
            Set_Data1(i, i, data_ct, data, pply, et);
        }
    }
    // スキャン・ライン・アルゴリズムによる塗り潰し
    aet_ct = 0;    // AET の要素数
    stride_sum = y_min * gnStride;
    for(i=y_min; i<=y_max; i++){
        // スキャンライン上の頂点を検索
        if(et[i].index != -1){
            aet[aet_ct ++] = data[et[i].index];
            next2 = data[et[i].index].next;
            if(next2 != -1){
                aet[aet_ct ++] = data[next2];
                next2 = data[next2].next;
            }
        }
        // AET を x について昇順にソート
        if(aet[0].x > aet[1].x){
            EDGE e = aet[0];
            aet[0] = aet[1];
            aet[1] = e;
        }
        // X座標のクリッピング
        if(aet[0].x < 0){
            aet[0].x = 0;
        }
        if(aet[1].x >= WIDTH){
            aet[1].x = WIDTH - 1;
        }
        // スキャンラインの描画
        LPBYTE  pb = gpBitsWork + stride_sum + aet[0].x * COLOR_BYTE;
        for(j=aet[0].x;j<aet[1].x;j++){
            Set_ValRate(&rate, j, aet[0].x, aet[1].x);
            Set_Z(&z, &rate, aet[0].z, aet[1].z);
            if(z < gZBuf[i][j]){    // ピクセルごとの Z バッファ判定
                gZBuf[i][j] = z;
                // 照明色
                Set_Color(&argbLight, &rate, aet[0].color, aet[1].color);
                BYTE r = (BYTE) argbLight.r;
                BYTE g = (BYTE) argbLight.g;
                BYTE b = (BYTE) argbLight.b;
                // ピクセル値設定
                *pb ++ = b;
                *pb ++ = g;
                *pb ++ = r;
            }
            else{
                pb += COLOR_BYTE;
            }
        }
        stride_sum += gnStride;
        // i == y_max の要素を AET から削除
        if(i == aet[1].y_max){
            aet_ct --;
        }
        if(i == aet[0].y_max){
            aet[0] = aet[1];
            aet_ct --;
        }
        // AET の各 x を次のスキャンラインでの値に更新
        for(j=0;j<2;j++){
            y_next = i + 1;
            aet[j].x = Round(aet[j].x_min + aet[j].slant_sum);
            aet[j].slant_sum += aet[j].slant;
            Set_ValRate(&rate, y_next, aet[j].y_min, aet[j].y_max);
            Set_Color(&aet[j].color, &rate, aet[j].c_min, aet[j].c_max);
            Set_Z(&aet[j].z, &rate, aet[j].z_min, aet[j].z_max);
        }
    }
}// Paint_Triangle

// 比率設定
void Set_ValRate(LPRATE rate, int idx, int idx_min, int idx_max)
{
    if(idx_min != idx_max){
        rate->f1 = (float)(idx - idx_min) / (idx_max - idx_min);
        rate->f2 = 1 - rate->f1;
        rate->n1 = (int)(rate->f1 * MULT2);
        rate->n2 = MULT2 - rate->n1;
    }
    else{
        rate->f1 = -1;
        rate->n1 = -1;
    }
}// Set_ValRate

// 色設定
void Set_Color(LPARGBN color, LPRATE rate, ARGBN c_min, ARGBN c_max)
{
    if(rate->n1 != -1){
        color->a = gMultTbl[c_max.a][rate->n1] + gMultTbl[c_min.a][rate->n2];
        color->r = gMultTbl[c_max.r][rate->n1] + gMultTbl[c_min.r][rate->n2];
        color->g = gMultTbl[c_max.g][rate->n1] + gMultTbl[c_min.g][rate->n2];
        color->b = gMultTbl[c_max.b][rate->n1] + gMultTbl[c_min.b][rate->n2];
    }
    else{
        *color = c_min;
    }
}// Set_Color

// 初期化
void M_Create()
{
    // 立方体作成
    Create_Cube();
    // 面・頂点法線ベクトル設定
    Set_Normal();
    // 掛け算対応表作成
    int i,j;
    for(i=0; i<=MULT1; i++){
        for(j=0; j<=MULT2; j++){
            gMultTbl[i][j]  = ((i * j) / MULT2);
        }
    }
}// M_Create
	
トップへ 前へ 次へ

 テクスチャ表示

テクスチャ座標の取り扱いは、基本的に照明色やZ値と同じ。
上のコードに以下の赤字部分を置換または追加する。
typedef struct{
        float   x;
        float   y;
}VEC2,*LPVEC2;

typedef struct{
    POINT       pos;    // 頂点座標
    ARGBN       c1;     // 頂点ディフューズ色
    ARGBN       c2;     // 頂点スペキュラ色
    float       z;      // 頂点Z値
    VEC2         t;      // テクスチャ座標
}POLYPAINT,*LPPOLYPAINT;

typedef struct{
    int         y_min;  // 辺の最小Y座標
    int         y_max;  // 辺の最大Y座標
    int         x_min;  // 辺の最小Y座標のX座標
    int         x;      // スキャンラインと辺の交点のX座標
    double      slant;  // 辺の傾きの逆数の整数部
    double      slant_sum;  // slantの和
    ARGBN       c1_min; // 辺の最小Y座標のディフューズ色
    ARGBN       c1_max; // 辺の最大Y座標のディフューズ色
    ARGBN       color1; // ディフューズ色
    ARGBN       c2_min; // 辺の最小Y座標のスペキュラ色
    ARGBN       c2_max; // 辺の最大Y座標のスペキュラ色
    ARGBN       color2; // スペキュラ色
    float       z_min;  // 辺の最小Y座標のZ値
    float       z_max;  // 辺の最大Y座標のZ値
    float       z;      // Z値
    VEC2        t_min;  // 辺の最小Y座標のテクスチャ座標
    VEC2        t_max;  // 辺の最大Y座標のテクスチャ座標
    VEC2        texcrd; // テクスチャ座標
    int         next;   // 次のデータ番号
}EDGE,*LPEDGE;

typedef struct{
    VEC3        v[8];           // 頂点座標
    VEC3        n[8];           // 頂点法線ベクトル
    VEC2        t[8];           // テクスチャ座標
    int         f[6][4];        // 各面の頂点番号
    VEC3        fn[6];          // 面法線ベクトル
}MODEL,*LPMODEL;

typedef struct{
    HBITMAP hBmp;
    UINT    width,height,stride;
    LPDWORD pBits;
}TEX,*LPTEX;

TEX         gTex;                       // テクスチャ情報
const RGBB  gAmbient = {10, 10, 10};    // 環境光(各色 0 〜 255)
const int   gSpecularPower = 3;         // スペキュラ反射の強さ

void Create_Cube();
void Draw_Cube(HDC hDC, VEC3 v_sun);
void Paint_Triangle(HDC hDC, LPPOLYPAINT pp);
void Set_Data1(int idx_min, int idx_max, int &data_ct, LPEDGE data, LPPOLYPAINT pply, LPET et);
void Set_Data2(int idx_min, int idx_max, int &data_ct, LPEDGE data, LPPOLYPAINT pply, LPET et);
void Set_TexCrd(VEC2 *texcrd, LPRATE rate, VEC2 t_min, VEC2 t_max);
void M_Create();
void M_Paint(HDC hDC);
void M_Close();

// 立方体の作成
void Create_Cube()
{
    VEC3    v[8]    =    {{0.5f,0.5f,-0.5f},{0.5f,0.5f,0.5f},{-0.5f,0.5f,0.5f},
                          {-0.5f,0.5f,-0.5f},{0.5f,-0.5f,-0.5f},{0.5f,-0.5f,0.5f},
                          {-0.5f,-0.5f,0.5f},{-0.5f,-0.5f,-0.5f}};
    VEC2    t[8]    =    {{0,0},{0,0},{1,0},{1,0},{0,1},{0,1},{1,1},{1,1}};
    int     f[6][4] =    {{3,2,1,0},{0,1,5,4},{1,2,6,5},{2,3,7,6},{3,0,4,7},{4,5,6,7}};
    memcpy(gmCube.v, v, sizeof(gmCube.v));
    memcpy(gmCube.t, t, sizeof(gmCube.t));
    memcpy(gmCube.f, f, sizeof(gmCube.f));
}// Create_Cube

// 立方体描画
void Draw_Cube(HDC hDC, VEC3 v_sun)
{
    int         i,j;
    VEC3 v_wv[4],v_all[4];
    const float d_perspect = HEIGHT;                // 透視投象での投影するスクリーンと視点の距離
    const POINT offset = {WIDTH / 2, HEIGHT / 2};   // 投象後の二次元座標の平行移動幅
    const float power1 = 0.7f;                      // 反射の強さ(0 〜 1)
    const RGBB  diffuse = {255, 255, 255};          // 面のディフューズ色(各色 0 〜 255)
    const RGBB  specular = {255, 255, 255};         // 面のスペキュラ色(各色 0 〜 255)
    float       power2,powerDiffuse,powerSpecular;
    ARGBN       argbDiffuse,argbSpecular;
    POLYPAINT   ply1[4];                            // 4頂点ポリゴン色塗り情報
    POLYPAINT   ply2[2][3];                         // 3頂点ポリゴン色塗り情報
    
    gMtWorldView    = gMtWorld * gMtView;
    gMtAll          = gMtWorldView * gMtProj;
    
    for(i=0; i<6; i++){
        for(j=0; j<4; j++){
            int  index = gmCube.f[i][j];
            // 頂点のワールド・ビュー行列変換
            v_wv[j] = Vec3Normalize(MtrxVec3(gmCube.v[index], gMtWorldView));
        }
        // 面法線ベクトル計算(カリング用)
        VEC3 fn_cull = Get_FaceNormal(v_wv);
        // カリング
        float   dot_cull = Vec3Dot(v_look, fn_cull);
        if(dot_cull <= 0){  // 片面表示
            for(j=0; j<4; j++){
                int  index = gmCube.f[i][j];
                // 頂点のワールド・ビュー・プロジェクション行列変換
                v_all[j] = MtrxVec3(gmCube.v[index], gMtAll);
                // 頂点法線ベクトルのワールド行列変換
                VEC3    n_color;
                n_color = MtrxVec3(gmCube.n[index], gMtWorld);
                n_color = Vec3Normalize(n_color);
                // Lambert 照明モデルによる拡散反射色設定
                power2 = Vec3Dot(v_sun, n_color);
                powerDiffuse = power1 * power2;
                if(powerDiffuse < 0){
                    powerDiffuse = 0;
                }
                argbDiffuse.a = 0;
                argbDiffuse.r = (UINT)(diffuse.r * powerDiffuse);
                argbDiffuse.g = (UINT)(diffuse.g * powerDiffuse);
                argbDiffuse.b = (UINT)(diffuse.b * powerDiffuse);
                if(argbDiffuse.r > 255) argbDiffuse.r = 255;
                if(argbDiffuse.g > 255) argbDiffuse.g = 255;
                if(argbDiffuse.b > 255) argbDiffuse.b = 255;
                // 簡易 Phong 照明モデルによるスペキュラ反射色設定
                powerSpecular = power1 * (float) pow(power2, (int) gSpecularPower);
                if(powerSpecular < 0){
                    powerSpecular = 0;
                }
                argbSpecular.a = 0;
                argbSpecular.r = (UINT)(specular.r * powerSpecular);
                argbSpecular.g = (UINT)(specular.g * powerSpecular);
                argbSpecular.b = (UINT)(specular.b * powerSpecular);
                if(argbSpecular.r > 255)    argbSpecular.r = 255;
                if(argbSpecular.g > 255)    argbSpecular.g = 255;
                if(argbSpecular.b > 255)    argbSpecular.b = 255;
                float size_rate = d_perspect;
                if(gFlagPerspect)
                    size_rate /= (v_all[j].z + gPosLookAt.z - gPosLook.z);
                ply1[j].pos.x   = (long)( v_all[j].x * size_rate + offset.x);
                ply1[j].pos.y   = (long)(-v_all[j].y * size_rate + offset.y);
                ply1[j].z       = v_all[j].z;
                ply1[j].c1.a    = argbDiffuse.a;
                ply1[j].c1.r    = argbDiffuse.r;
                ply1[j].c1.g    = argbDiffuse.g;
                ply1[j].c1.b    = argbDiffuse.b;
                ply1[j].c2.a    = argbSpecular.a;
                ply1[j].c2.r    = argbSpecular.r;
                ply1[j].c2.g    = argbSpecular.g;
                ply1[j].c2.b    = argbSpecular.b;
                ply1[j].t       = gmCube.t[index];
            }
            ply2[0][0] = ply1[0];
            ply2[0][1] = ply1[1];
            ply2[0][2] = ply1[2];
            ply2[1][0] = ply1[0];
            ply2[1][1] = ply1[2];
            ply2[1][2] = ply1[3];
            Paint_Triangle(hDC, ply2[0]);
            Paint_Triangle(hDC, ply2[1]);
        }
    }
}// Draw_Cube

// 三角形塗り
// pply     : 三角形の頂点と色のリスト
void Paint_Triangle(HDC hDC, LPPOLYPAINT pply)
{
    EDGE        data[3];    // 頂点リスト
    EDGE        aet[2];     // アクティブ辺テーブル(AET)
    ET          et[HEIGHT]; // 辺テーブル(ET)
    int         i,j,y_min,y_max,prev,next,next2,data_ct,aet_ct,y_next;
    UINT        stride_sum;
    float       z = 0;      // Z 値
    ARGBN       argbDiffuse,argbSpecular,argbTex,argbVtx;
    VEC2        texcrd;
    RATE        rate;
    
    // Y座標のクリッピング
    for(i=0; i<3; i++){
        if(pply[i].pos.y < 0)
            pply[i].pos.y = 0;
        if(pply[i].pos.y >= HEIGHT)
            pply[i].pos.y = HEIGHT - 1;
    }
    // Y座標の最大値と最小値を求める
    y_min = pply[0].pos.y;
    y_max = pply[0].pos.y;
    for(i=1; i<3; i++){
        if(pply[i].pos.y < y_min){
            y_min = pply[i].pos.y;
        }
        else if(pply[i].pos.y > y_max)
            y_max = pply[i].pos.y;
    }
    // 頂点リスト初期化
    for(i=0; i<3; i++){
        data[i].next = -1;
    }
    // ET 初期化
    for(i=y_min; i<=y_max; i++){
        et[i].index = -1;
        et[i].last = -1;
    }
    data_ct = 0;    // data の要素数
    for(i=0; i<3; i++){
        prev = i - 1;
        if(prev < 0){
            prev = 2;
        }
        next = i + 1;
        if(next >= 3){
            next = 0;
        }
        if(pply[i].pos.y < pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data2(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data2(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y == pply[prev].pos.y){
            Set_Data1(i, i, data_ct, data, pply, et);
        }
    }
    // スキャン・ライン・アルゴリズムによる塗り潰し
    aet_ct = 0; // AET の要素数
    stride_sum = y_min * gnStride;
    for(i=y_min; i<=y_max; i++){
        // スキャンライン上の頂点を検索
        if(et[i].index != -1){
            aet[aet_ct ++] = data[et[i].index];
            next2 = data[et[i].index].next;
            if(next2 != -1){
                aet[aet_ct ++] = data[next2];
                next2 = data[next2].next;
            }
        }
        // AET を x について昇順にソート
        if(aet[0].x > aet[1].x){
            EDGE e = aet[0];
            aet[0] = aet[1];
            aet[1] = e;
        }
        // X座標のクリッピング
        if(aet[0].x < 0){
            aet[0].x = 0;
        }
        if(aet[1].x >= WIDTH){
            aet[1].x = WIDTH - 1;
        }
        // スキャンラインの描画
        LPBYTE  pb = gpBitsWork + stride_sum + aet[0].x * COLOR_BYTE;
        for(j=aet[0].x;j<aet[1].x;j++){
            Set_ValRate(&rate, j, aet[0].x, aet[1].x);
            Set_Z(&z, &rate, aet[0].z, aet[1].z);
            if(z < gZBuf[i][j])   // ピクセルごとの Z バッファ判定
            {
                gZBuf[i][j] = z;
                // ディフーズ色
                Set_Color(&argbDiffuse, &rate, aet[0].color1, aet[1].color1);
                // スペキュラ色
                Set_Color(&argbSpecular, &rate, aet[0].color2, aet[1].color2);
                if(gSpecularPower > 0){
                    argbVtx = argbSpecular;
                }
                else{
                    argbVtx.a = argbVtx.r = argbVtx.g = argbVtx.b = 0;
                }
                // テクスチャ座標
                Set_TexCrd(&texcrd, &rate, aet[0].texcrd, aet[1].texcrd);
                if(texcrd.x < 0)
                    texcrd.x -= (int)(texcrd.x - 1);
                else if(texcrd.x > 1)
                    texcrd.x -= (int) texcrd.x;
                if(texcrd.y < 0)
                    texcrd.y -= (int)(texcrd.y - 1);
                else if(texcrd.y > 1)
                    texcrd.y -= (int) texcrd.y;
                // テクスチャ色
                int     xTex = (int)(texcrd.x * (gTex.width - 1));
                int     yTex = (int)(texcrd.y * (gTex.height - 1));
                DWORD   val = gTex.pBits[gTex.width * yTex + xTex];
                argbTex.r = (BYTE)(val >> 16);
                argbTex.g = (BYTE)(val >> 8);
                argbTex.b = (BYTE)(val);
                // テクスチャに濃淡を設定
                argbTex.r = gMultTbl[argbTex.r][argbDiffuse.r];
                argbTex.g = gMultTbl[argbTex.g][argbDiffuse.g];
                argbTex.b = gMultTbl[argbTex.b][argbDiffuse.b];
                // テクスチャにハイライトを設定
                argbVtx.r += argbTex.r;
                argbVtx.g += argbTex.g;
                argbVtx.b += argbTex.b;
                // 環境光設定
                argbVtx.r += gAmbient.r;
                argbVtx.g += gAmbient.g;
                argbVtx.b += gAmbient.b;
                if(argbVtx.r < 0)           argbVtx.r = 0;
                else if(argbVtx.r > 255)    argbVtx.r = 255;
                if(argbVtx.g < 0)           argbVtx.g = 0;
                else if(argbVtx.g > 255)    argbVtx.g = 255;
                if(argbVtx.b < 0)           argbVtx.b = 0;
                else if(argbVtx.b > 255)    argbVtx.b = 255;
                // ピクセル値設定
                *pb ++ = (BYTE) argbVtx.b;
                *pb ++ = (BYTE) argbVtx.g;
                *pb ++ = (BYTE) argbVtx.r;
            }
            else{
                pb += COLOR_BYTE;
            }
        }
        stride_sum += gnStride;
        // i == y_max の要素を AET から削除
        if(i == aet[1].y_max){
            aet_ct --;
        }
        if(i == aet[0].y_max){
            aet[0] = aet[1];
            aet_ct --;
        }
        // AET の各 x を次のスキャンラインでの値に更新
        for(j=0;j<2;j++){
            y_next = i + 1;
            aet[j].x = (int)(aet[j].x_min + aet[j].slant_sum);
            aet[j].slant_sum += aet[j].slant;
            Set_ValRate(&rate, y_next, aet[j].y_min, aet[j].y_max);
            Set_Color(&aet[j].color1, &rate, aet[j].c1_min, aet[j].c1_max);
            Set_Color(&aet[j].color2, &rate, aet[j].c2_min, aet[j].c2_max);
            Set_Z(&aet[j].z, &rate, aet[j].z_min, aet[j].z_max);
            Set_TexCrd(&aet[j].texcrd, &rate, aet[j].t_min, aet[j].t_max);
        }
    }
}// Paint_Triangle

// 頂点リスト設定1
void Set_Data1(int idx_min, int idx_max, int &data_ct, LPEDGE data, LPPOLYPAINT pply, LPET et)
{
    int et_idx = pply[idx_min].pos.y;
    if(et[et_idx].last == -1)
        et[et_idx].index = data_ct;
    else
        data[et[et_idx].last].next = data_ct;
    et[et_idx].last = data_ct;
    
    data[data_ct].y_min = pply[idx_min].pos.y;
    data[data_ct].y_max = pply[idx_max].pos.y;
    data[data_ct].x_min = pply[idx_min].pos.x;
    data[data_ct].x     = data[data_ct].x_min;
    data[data_ct].slant = (double)(pply[idx_max].pos.x - pply[idx_min].pos.x) / (pply[idx_max].pos.y - pply[idx_min].pos.y);
    data[data_ct].slant_sum = data[data_ct].slant;
    data[data_ct].c1_min = pply[idx_min].c1;
    data[data_ct].c1_max = pply[idx_max].c1;
    data[data_ct].color1 = data[data_ct].c1_min;
    data[data_ct].c2_min = pply[idx_min].c2;
    data[data_ct].c2_max = pply[idx_max].c2;
    data[data_ct].color2 = data[data_ct].c2_min;
    data[data_ct].z_min = pply[idx_min].z;
    data[data_ct].z_max = pply[idx_max].z;
    data[data_ct].z     = data[data_ct].z_min;
    data[data_ct].t_min = pply[idx_min].t;
    data[data_ct].t_max = pply[idx_max].t;
    data[data_ct].texcrd = data[data_ct].t_min;
    data_ct ++;
}// Set_Data1

// 頂点リスト設定2
void Set_Data2(int idx_min, int idx_max, int &data_ct, LPEDGE data, LPPOLYPAINT pply, LPET et)
{
    int et_idx = pply[idx_min].pos.y + 1;
    if(et[et_idx].last == -1)
        et[et_idx].index = data_ct;
    else
        data[et[et_idx].last].next = data_ct;
    et[et_idx].last = data_ct;
    
    double  slant = (double)(pply[idx_max].pos.x - pply[idx_min].pos.x) / (pply[idx_max].pos.y - pply[idx_min].pos.y);
    RATE        rate;
    VEC2        texcrd;
    float       z;
    ARGBN       color1,color2;
    Set_ValRate(&rate, et_idx, pply[idx_min].pos.y, pply[idx_max].pos.y);
    data[data_ct].y_min = pply[idx_min].pos.y;
    data[data_ct].y_max = pply[idx_max].pos.y;
    data[data_ct].x_min = pply[idx_min].pos.x;
    data[data_ct].x     = (int)((float) data[data_ct].x_min + slant);
    data[data_ct].slant = slant;
    data[data_ct].slant_sum = data[data_ct].slant + slant;
    Set_Color(&color1, &rate, pply[idx_min].c1, pply[idx_max].c1);
    data[data_ct].c1_min = pply[idx_min].c1;
    data[data_ct].c1_max = pply[idx_max].c1;
    data[data_ct].color1 = color1;
    Set_Color(&color2, &rate, pply[idx_min].c2, pply[idx_max].c2);
    data[data_ct].c2_min = pply[idx_min].c2;
    data[data_ct].c2_max = pply[idx_max].c2;
    data[data_ct].color2 = color2;
    Set_Z(&z, &rate, pply[idx_min].z, pply[idx_max].z);
    data[data_ct].z_min = pply[idx_min].z;
    data[data_ct].z_max = pply[idx_max].z;
    data[data_ct].z     = z;
    Set_TexCrd(&texcrd, &rate, pply[idx_min].t, pply[idx_max].t);
    data[data_ct].t_min = pply[idx_min].t;
    data[data_ct].t_max = pply[idx_max].t;
    data[data_ct].texcrd = texcrd;
    data_ct ++;
}// Set_Data2

// テクスチャ座標設定
void Set_TexCrd(VEC2 *texcrd, LPRATE rate, VEC2 t_min, VEC2 t_max)
{
    if(rate->f1 != -1){
        texcrd->x = t_max.x * rate->f1 + t_min.x * rate->f2;
        texcrd->y = t_max.y * rate->f1 + t_min.y * rate->f2;
    }
    else{
        *texcrd = t_min;
    }
}// Set_TexCrd

// 初期化
void M_Create()
{
    // 立方体作成
    Create_Cube();
    // 面・頂点法線ベクトル設定
    Set_Normal();
    // 掛け算対応表作成
    int i,j;
    for(i=0; i<=MULT1; i++){
        for(j=0; j<=MULT2; j++){
            gMultTbl[i][j]  = ((i * j) / MULT2);
        }
    }
    // テクスチャ ファイル
    BITMAP      bmp;
    BITMAPINFO  bi;
    char *p;
    char path[MAX_PATH];
    GetModuleFileName(NULL, path, sizeof(path));
    p = strrchr(path, '\\');
    strcpy(p+1, "tex1.bmp");
    HBITMAP hBmp = (HBITMAP) LoadImage(NULL, path, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
    GetObject(hBmp, sizeof(bmp), &bmp);
    ZeroMemory(&bi, sizeof(bi));
    bi.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
    bi.bmiHeader.biWidth        = bmp.bmWidth;
    bi.bmiHeader.biHeight       = - bmp.bmHeight;
    bi.bmiHeader.biPlanes       = 1;
    bi.bmiHeader.biBitCount     = 32;
    bi.bmiHeader.biCompression  = BI_RGB;
    gTex.hBmp = (HBITMAP) CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, (VOID**) &gTex.pBits, NULL, 0);
    HDC     hdcCp1,hdcCp2;
    HBITMAP hbmOld1,hbmOld2;
    hdcCp1 = CreateCompatibleDC(NULL);
    hdcCp2 = CreateCompatibleDC(NULL);
    hbmOld1 = (HBITMAP)SelectObject(hdcCp1, gTex.hBmp);
    hbmOld2 = (HBITMAP)SelectObject(hdcCp2, hBmp);
    BitBlt(hdcCp1, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcCp2, 0, 0, SRCCOPY);
    SelectObject(hdcCp1, hbmOld1);
    SelectObject(hdcCp2, hbmOld2);
    DeleteDC(hdcCp1);
    DeleteDC(hdcCp2);
    DeleteObject(hBmp);
    gTex.width  = bmp.bmWidth;
    gTex.height = bmp.bmHeight;
    gTex.stride = WIDTHBYTES(bi.bmiHeader.biWidth * bi.bmiHeader.biBitCount);
}// M_Create

// 描画
void M_Paint(HDC hDC)
{
    const VEC3  pos_look = {0.0f, 2.0f, -2.0f};     // 視点の位置
    const VEC3  pos_lookat = {0.0f, 0.0f, 0.0f};    // 注視点の位置
    const VEC3  v_lookup = {0, 1, 0};               // 視点の上向き
    VEC3        v_sun = {0.0f, -1.0f, 1.0f};        // 入射光のベクトル
    const RGBB  material = {255, 255, 255};         // 面のディフューズ色(各色 0 〜 255)
    const RGBB  ambient = {10, 10, 10};             // 環境光(各色 0 〜 255)
    MTRX        mtScal,mtRotY,mtTrans,mtWork;
    static int  roll_y = 0;                         // 回転角度
    
    // 行列設定
    gMtView     = MtrxView(pos_look, pos_lookat, v_lookup);
    gMtProj     = MtrxPerspect(0.0f, 10.0f, 3.14f / 4, 1.0f);
//  gMtProj     = MtrxOrtho(0.0f, 10.0f, 5.0f, 5.0f);
    // 入射光ベクトル
    v_sun.x     = - v_sun.x;
    v_sun.y     = - v_sun.y;
    v_sun.z     = - v_sun.z;
    v_sun       = Vec3Normalize(v_sun);
    // Zバッファ初期化
    Clear_ZBuf();
    // オブジェクト描画
    mtScal      = MtrxScal(1.0f, 1.0f, 1.0f);
    mtRotY      = MtrxRotY(roll_y * 3.14f / 180);
    mtWork      = MtrxMult(mtScal, mtRotY);
    mtTrans     = MtrxTranslate(-0.2f, 0.2f, 0.0f);
    gMtWorld    = MtrxMult(mtWork, mtTrans);
    Draw_Cube(hDC, v_sun);
    mtTrans     = MtrxTranslate(0.2f, 0.0f,-0.2f);
    gMtWorld    = MtrxMult(mtWork, mtTrans);
    Draw_Cube(hDC, v_sun);
    // 回転角度増
    roll_y += 10;
    if(roll_y >= 360)
        roll_y = 0;
}// M_Paint

// 終了処理
void M_Close()
{
    DeleteObject(gTex.hBmp);
}// M_Close
	
トップへ 前へ 次へ

 マテリアル アルファ ブレンディング

マテリアルに透明度情報を付加し、半透明描画する。
テーブルを使って高速化した。
上のコードに以下の赤字部分を置換または追加する。
typedef struct{
    BYTE    a;
    BYTE    r;
    BYTE    g;
    BYTE    b;
}ARGBB,*LPARGBB;

void Draw_Cube(HDC hDC, VEC3 v_sun);
void Paint_Triangle(HDC hDC, LPPOLYPAINT pp);

// 立方体描画
void Draw_Cube(HDC hDC, VEC3 v_sun)
{
    int         i,j;
    VEC3        v_wv[4],v_all[4];
    VEC3        v_look;                             // 注視点から視点へのベクトル
    const int   d_perspect = HEIGHT;                // 透視投象での投影するスクリーンと視点の距離
    const POINT offset = {WIDTH / 2, HEIGHT / 2};   // 投象後の二次元座標の平行移動幅
    const float power1 = 0.7f;                      // 反射の強さ(0 〜 1)
    const ARGBB	diffuse = {127, 255, 255, 255};     // 面のディフューズ色(各色 0 〜 255)
    const RGBB  specular = {255, 255, 255};         // 面のスペキュラ色(各色 0 〜 255)
    float       power2,powerDiffuse,powerSpecular;
    ARGBN       argbDiffuse,argbSpecular;
    POLYPAINT   ply1[4];                            // 4頂点ポリゴン色塗り情報
    POLYPAINT   ply2[2][3];                         // 3頂点ポリゴン色塗り情報
    
    gMtWorldView    = MtrxMult(gMtWorld, gMtView);
    gMtAll          = MtrxMult(gMtWorldView, gMtProj);
    
    // 視線ベクトル
    VEC3    posLookAt = MtrxVec3(gPosLookAt, gMtWorld);
    v_look  = Vec3Normalize(MtrxVec3(Vec3Sub(posLookAt, gPosLook), gMtView));
    
    for(i=0; i<6; i++){
        for(j=0; j<4; j++){
            int  index = gmCube.f[i][j];
            // 頂点のワールド・ビュー行列変換
            v_wv[j] = Vec3Normalize(MtrxVec3(gmCube.v[index], gMtWorldView));
        }
        // 面法線ベクトル計算(カリング用)
        VEC3    fn_cull = Get_FaceNormal(v_wv);
        // カリング
        float   dot_cull = Vec3Dot(v_look, fn_cull);
        if(dot_cull < 0){   // 片面表示
            for(j=0; j<4; j++){
                int  index = gmCube.f[i][j];
                // 頂点のワールド・ビュー・プロジェクション行列変換
                v_all[j] = MtrxVec3(gmCube.v[index], gMtAll);
                // 頂点法線ベクトルのワールド行列変換
                VEC3    n_color;
                n_color = MtrxVec3(gmCube.n[index], gMtWorld);
                n_color = Vec3Normalize(n_color);
                // Lambert 照明モデルによる拡散反射色設定
                power2 = Vec3Dot(v_sun, n_color);
                powerDiffuse = power1 * power2;
                if(powerDiffuse < 0){
                    powerDiffuse = 0;
                }
                argbDiffuse.a = diffuse.a;
                argbDiffuse.r = (UINT)(diffuse.r * powerDiffuse);
                argbDiffuse.g = (UINT)(diffuse.g * powerDiffuse);
                argbDiffuse.b = (UINT)(diffuse.b * powerDiffuse);
                if(argbDiffuse.r > 255) argbDiffuse.r = 255;
                if(argbDiffuse.g > 255) argbDiffuse.g = 255;
                if(argbDiffuse.b > 255) argbDiffuse.b = 255;
                // 簡易 Phong 照明モデルによるスペキュラ反射色設定
                powerSpecular = power1 * (float) pow(power2, (int) gSpecularPower);
                if(powerSpecular < 0){
                    powerSpecular = 0;
                }
                argbSpecular.a = diffuse.a;
                argbSpecular.r = (UINT)(specular.r * powerSpecular);
                argbSpecular.g = (UINT)(specular.g * powerSpecular);
                argbSpecular.b = (UINT)(specular.b * powerSpecular);
                if(argbSpecular.r > 255)    argbSpecular.r = 255;
                if(argbSpecular.g > 255)    argbSpecular.g = 255;
                if(argbSpecular.b > 255)    argbSpecular.b = 255;
                float size_rate = d_perspect;
                if(gFlagPerspect)
                    size_rate /= (v_all[j].z + gPosLookAt.z - gPosLook.z);
                ply1[j].pos.x   = (long)( v_all[j].x * size_rate + offset.x);
                ply1[j].pos.y   = (long)(-v_all[j].y * size_rate + offset.y);
                ply1[j].z       = v_all[j].z;
                ply1[j].c1.a    = argbDiffuse.a;
                ply1[j].c1.r    = argbDiffuse.r;
                ply1[j].c1.g    = argbDiffuse.g;
                ply1[j].c1.b    = argbDiffuse.b;
                ply1[j].c2.a    = argbSpecular.a;
                ply1[j].c2.r    = argbSpecular.r;
                ply1[j].c2.g    = argbSpecular.g;
                ply1[j].c2.b    = argbSpecular.b;
                ply1[j].t       = gmCube.t[index];
            }
            ply2[0][0] = ply1[0];
            ply2[0][1] = ply1[1];
            ply2[0][2] = ply1[2];
            ply2[1][0] = ply1[0];
            ply2[1][1] = ply1[2];
            ply2[1][2] = ply1[3];
            Paint_Triangle(hDC, ply2[0]);
            Paint_Triangle(hDC, ply2[1]);
        }
    }
}// Draw_Cube

// 三角形塗り
// pply     : 三角形の頂点と色のリスト
void Paint_Triangle(HDC hDC, LPPOLYPAINT pply)
{
    EDGE        data[3];    // 頂点リスト
    EDGE        aet[2];     // アクティブ辺テーブル(AET)
    ET          et[HEIGHT]; // 辺テーブル(ET)
    int         i,j,y_min,y_max,prev,next,next2,data_ct,aet_ct,y_next;
    UINT        stride_sum;
    float       z = 0;      // Z 値
    ARGBN       argbDiffuse,argbSpecular,argbTex,argbVtx,argbBase;
    VEC2        texcrd;
    RATE        rate;
    
    // Y座標のクリッピング
    for(i=0; i<3; i++){
        if(pply[i].pos.y < 0)
            pply[i].pos.y = 0;
        if(pply[i].pos.y >= HEIGHT)
            pply[i].pos.y = HEIGHT - 1;
    }
    // Y座標の最大値と最小値を求める
    y_min = pply[0].pos.y;
    y_max = pply[0].pos.y;
    for(i=1; i<3; i++){
        if(pply[i].pos.y < y_min){
            y_min = pply[i].pos.y;
        }
        else if(pply[i].pos.y > y_max)
            y_max = pply[i].pos.y;
    }
    // 頂点リスト初期化
    for(i=0; i<3; i++){
        data[i].next = -1;
    }
    // ET 初期化
    for(i=y_min; i<=y_max; i++){
        et[i].index = -1;
        et[i].last = -1;
    }
    data_ct = 0;    // data の要素数
    for(i=0; i<3; i++){
        prev = i - 1;
        if(prev < 0){
            prev = 2;
        }
        next = i + 1;
        if(next >= 3){
            next = 0;
        }
        if(pply[i].pos.y < pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data2(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data2(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y == pply[prev].pos.y){
            Set_Data1(i, i, data_ct, data, pply, et);
        }
    }
    // スキャン・ライン・アルゴリズムによる塗り潰し
    aet_ct = 0; // AET の要素数
    stride_sum = y_min * gnStride;
    for(i=y_min; i<=y_max; i++){
        // スキャンライン上の頂点を検索
        if(et[i].index != -1){
            aet[aet_ct ++] = data[et[i].index];
            next2 = data[et[i].index].next;
            if(next2 != -1){
                aet[aet_ct ++] = data[next2];
                next2 = data[next2].next;
            }
        }
        // AET を x について昇順にソート
        if(aet[0].x > aet[1].x){
            EDGE e = aet[0];
            aet[0] = aet[1];
            aet[1] = e;
        }
        // X座標のクリッピング
        if(aet[0].x < 0){
            aet[0].x = 0;
        }
        if(aet[1].x >= WIDTH){
            aet[1].x = WIDTH - 1;
        }
        // スキャンラインの描画
        LPBYTE  pb = gpBitsWork + stride_sum + aet[0].x * COLOR_BYTE;
        for(j=aet[0].x;j<aet[1].x;j++){
            Set_ValRate(&rate, j, aet[0].x, aet[1].x);
            Set_Z(&z, &rate, aet[0].z, aet[1].z);
            if(z < gZBuf[i][j]){    // ピクセルごとの Z バッファ判定
                gZBuf[i][j] = z;
                // ディフーズ色
                Set_Color(&argbDiffuse, &rate, aet[0].color1, aet[1].color1);
                // スペキュラ色
                Set_Color(&argbSpecular, &rate, aet[0].color2, aet[1].color2);
                if(gSpecularPower > 0){
                    argbVtx = argbSpecular;
                }
                else{
                    argbVtx.a = argbVtx.r = argbVtx.g = argbVtx.b = 0;
                }
                // テクスチャ座標
                Set_TexCrd(&texcrd, &rate, aet[0].texcrd, aet[1].texcrd);
                if(texcrd.x < 0)
                    texcrd.x -= (int)(texcrd.x - 1);
                else if(texcrd.x > 1)
                    texcrd.x -= (int) texcrd.x;
                if(texcrd.y < 0)
                    texcrd.y -= (int)(texcrd.y - 1);
                else if(texcrd.y > 1)
                    texcrd.y -= (int) texcrd.y;
                // テクスチャ色
                int     xTex = (int)(texcrd.x * (gTex.width - 1));
                int     yTex = (int)(texcrd.y * (gTex.height - 1));
                DWORD   val = gTex.pBits[gTex.width * yTex + xTex];
                argbTex.r = (BYTE)(val >> 16);
                argbTex.g = (BYTE)(val >> 8);
                argbTex.b = (BYTE)(val);
                // テクスチャに濃淡を設定
                argbTex.r = gMultTbl[argbTex.r][argbDiffuse.r];
                argbTex.g = gMultTbl[argbTex.g][argbDiffuse.g];
                argbTex.b = gMultTbl[argbTex.b][argbDiffuse.b];
                // テクスチャにハイライトを設定
                argbVtx.r += argbTex.r;
                argbVtx.g += argbTex.g;
                argbVtx.b += argbTex.b;
                // 環境光設定
                argbVtx.r += gAmbient.r;
                argbVtx.g += gAmbient.g;
                argbVtx.b += gAmbient.b;
                if(argbVtx.r < 0)
                    argbVtx.r = 0;
                else if(argbVtx.r > 255)
                    argbVtx.r = 255;
                if(argbVtx.g < 0)
                    argbVtx.g = 0;
                else if(argbVtx.g > 255)
                    argbVtx.g = 255;
                if(argbVtx.b < 0)
                    argbVtx.b = 0;
                else if(argbVtx.b > 255)
                    argbVtx.b = 255;
                // アルファ ブレンディング
                argbBase.a = MULT2 - argbVtx.a;
                argbBase.b = *(pb + 0);
                argbBase.g = *(pb + 1);
                argbBase.r = *(pb + 2);
                argbVtx.r = gMultTbl[argbVtx.r][argbVtx.a] + gMultTbl[argbBase.r][argbBase.a];
                argbVtx.g = gMultTbl[argbVtx.g][argbVtx.a] + gMultTbl[argbBase.g][argbBase.a];
                argbVtx.b = gMultTbl[argbVtx.b][argbVtx.a] + gMultTbl[argbBase.b][argbBase.a];
                // ピクセル値設定
                *pb ++ = (BYTE) argbVtx.b;
                *pb ++ = (BYTE) argbVtx.g;
                *pb ++ = (BYTE) argbVtx.r;
            }
            else{
                pb += COLOR_BYTE;
            }
        }
        stride_sum += gnStride;
        // i == y_max の要素を AET から削除
        if(i == aet[1].y_max){
            aet_ct --;
        }
        if(i == aet[0].y_max){
            aet[0] = aet[1];
            aet_ct --;
        }
        // AET の各 x を次のスキャンラインでの値に更新
        for(j=0;j<2;j++){
            y_next = i + 1;
            aet[j].x = Round(aet[j].x_min + aet[j].slant_sum);
            aet[j].slant_sum += aet[j].slant;
            Set_ValRate(&rate, y_next, aet[j].y_min, aet[j].y_max);
            Set_Color(&aet[j].color1, &rate, aet[j].c1_min, aet[j].c1_max);
            Set_Color(&aet[j].color2, &rate, aet[j].c2_min, aet[j].c2_max);
            Set_Z(&aet[j].z, &rate, aet[j].z_min, aet[j].z_max);
            Set_TexCrd(&aet[j].texcrd, &rate, aet[j].t_min, aet[j].t_max);
        }
    }
}// Paint_Triangle
	
トップへ 前へ 次へ

 テクスチャ アルファ ブレンディング

考え方は、マテリアルのアルファブレンディングとほぼ同じ。
テクスチャのアルファブレンディングには、ピクセルごとに透明度を設定できる利点がある。
透明度が設定できるテクスチャは、32bit 色だけである。
上のコードに以下の赤字部分を置換または追加する。
void Paint_Triangle(HDC hDC, LPPOLYPAINT pp);
void M_Create();

// 三角形塗り
// pply     : 三角形の頂点と色のリスト
void Paint_Triangle(HDC hDC, LPPOLYPAINT pply)
{
    EDGE        data[3];    // 頂点リスト
    EDGE        aet[2];     // アクティブ辺テーブル(AET)
    ET          et[HEIGHT]; // 辺テーブル(ET)
    int         i,j,y_min,y_max,prev,next,next2,data_ct,aet_ct,y_next;
    UINT        stride_sum;
    float       z = 0;      // Z 値
    ARGBN       argbDiffuse,argbSpecular,argbTex,argbVtx,argbBase;
    VEC2        texcrd;
    RATE        rate;
    
    // Y座標のクリッピング
    for(i=0; i<3; i++){
        if(pply[i].pos.y < 0)
            pply[i].pos.y = 0;
        if(pply[i].pos.y >= HEIGHT)
            pply[i].pos.y = HEIGHT - 1;
    }
    // Y座標の最大値と最小値を求める
    y_min = pply[0].pos.y;
    y_max = pply[0].pos.y;
    for(i=1; i<3; i++){
        if(pply[i].pos.y < y_min){
            y_min = pply[i].pos.y;
        }
        else if(pply[i].pos.y > y_max)
            y_max = pply[i].pos.y;
    }
    // 頂点リスト初期化
    for(i=0; i<3; i++){
        data[i].next = -1;
    }
    // ET 初期化
    for(i=y_min; i<=y_max; i++){
        et[i].index = -1;
        et[i].last = -1;
    }
    data_ct = 0;    // data の要素数
    for(i=0; i<3; i++){
        prev = i - 1;
        if(prev < 0){
            prev = 2;
        }
        next = i + 1;
        if(next >= 3){
            next = 0;
        }
        if(pply[i].pos.y < pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data2(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data2(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y == pply[prev].pos.y){
            Set_Data1(i, i, data_ct, data, pply, et);
        }
    }
    // スキャン・ライン・アルゴリズムによる塗り潰し
    aet_ct = 0; // AET の要素数
    stride_sum = y_min * gnStride;
    for(i=y_min; i<=y_max; i++){
        // スキャンライン上の頂点を検索
        if(et[i].index != -1){
            aet[aet_ct ++] = data[et[i].index];
            next2 = data[et[i].index].next;
            if(next2 != -1){
                aet[aet_ct ++] = data[next2];
                next2 = data[next2].next;
            }
        }
        // AET を x について昇順にソート
        if(aet[0].x > aet[1].x){
            EDGE e = aet[0];
            aet[0] = aet[1];
            aet[1] = e;
        }
        // X座標のクリッピング
        if(aet[0].x < 0){
            aet[0].x = 0;
        }
        if(aet[1].x >= WIDTH){
            aet[1].x = WIDTH - 1;
        }
        // スキャンラインの描画
        LPBYTE  pb = gpBitsWork + stride_sum + aet[0].x * COLOR_BYTE;
        for(j=aet[0].x;j<aet[1].x;j++){
            Set_ValRate(&rate, j, aet[0].x, aet[1].x);
            Set_Z(&z, &rate, aet[0].z, aet[1].z);
            if(z < gZBuf[i][j]){    // ピクセルごとの Z バッファ判定
                gZBuf[i][j] = z;
                // ディフーズ色
                Set_Color(&argbDiffuse, &rate, aet[0].color1, aet[1].color1);
                // スペキュラ色
                Set_Color(&argbSpecular, &rate, aet[0].color2, aet[1].color2);
                if(gSpecularPower > 0){
                    argbVtx = argbSpecular;
                }
                else{
                    argbVtx.a = argbVtx.r = argbVtx.g = argbVtx.b = 0;
                }
                // テクスチャ座標
                Set_TexCrd(&texcrd, &rate, aet[0].texcrd, aet[1].texcrd);
                if(texcrd.x < 0)
                    texcrd.x -= (int)(texcrd.x - 1);
                else if(texcrd.x > 1)
                    texcrd.x -= (int) texcrd.x;
                if(texcrd.y < 0)
                    texcrd.y -= (int)(texcrd.y - 1);
                else if(texcrd.y > 1)
                    texcrd.y -= (int) texcrd.y;
                // テクスチャ色
                int     xTex = (int)(texcrd.x * (gTex.width - 1));
                int     yTex = (int)(texcrd.y * (gTex.height - 1));
                DWORD   val = gTex.pBits[gTex.width * yTex + xTex];
                argbTex.a = (BYTE)(val >> 24);
                argbTex.r = (BYTE)(val >> 16);
                argbTex.g = (BYTE)(val >> 8);
                argbTex.b = (BYTE)(val);
                // テクスチャに濃淡を設定
                argbTex.r = gMultTbl[argbTex.r][argbDiffuse.r];
                argbTex.g = gMultTbl[argbTex.g][argbDiffuse.g];
                argbTex.b = gMultTbl[argbTex.b][argbDiffuse.b];
                // テクスチャにハイライトを設定
                argbVtx.r += argbTex.r;
                argbVtx.g += argbTex.g;
                argbVtx.b += argbTex.b;
                // 環境光設定
                argbVtx.r += gAmbient.r;
                argbVtx.g += gAmbient.g;
                argbVtx.b += gAmbient.b;
                if(argbVtx.r < 0)
                    argbVtx.r = 0;
                else if(argbVtx.r > 255)
                    argbVtx.r = 255;
                if(argbVtx.g < 0)
                    argbVtx.g = 0;
                else if(argbVtx.g > 255)
                    argbVtx.g = 255;
                if(argbVtx.b < 0)
                    argbVtx.b = 0;
                else if(argbVtx.b > 255)
                    argbVtx.b = 255;
                // アルファ ブレンディング
                argbVtx.a = gMultTbl[argbVtx.a][argbTex.a];
                argbBase.a = MULT2 - argbVtx.a;
                argbBase.b = *(pb + 0);
                argbBase.g = *(pb + 1);
                argbBase.r = *(pb + 2);
                argbVtx.r = gMultTbl[argbVtx.r][argbVtx.a] + gMultTbl[argbBase.r][argbBase.a];
                argbVtx.g = gMultTbl[argbVtx.g][argbVtx.a] + gMultTbl[argbBase.g][argbBase.a];
                argbVtx.b = gMultTbl[argbVtx.b][argbVtx.a] + gMultTbl[argbBase.b][argbBase.a];
                // ピクセル値設定
                *pb ++ = (BYTE) argbVtx.b;
                *pb ++ = (BYTE) argbVtx.g;
                *pb ++ = (BYTE) argbVtx.r;
            }
            else{
                pb += COLOR_BYTE;
            }
        }
        stride_sum += gnStride;
        // i == y_max の要素を AET から削除
        if(i == aet[1].y_max){
            aet_ct --;
        }
        if(i == aet[0].y_max){
            aet[0] = aet[1];
            aet_ct --;
        }
        // AET の各 x を次のスキャンラインでの値に更新
        for(j=0;j<2;j++){
            y_next = i + 1;
            aet[j].x = Round(aet[j].x_min + aet[j].slant_sum);
            aet[j].slant_sum += aet[j].slant;
            Set_ValRate(&rate, y_next, aet[j].y_min, aet[j].y_max);
            Set_Color(&aet[j].color1, &rate, aet[j].c1_min, aet[j].c1_max);
            Set_Color(&aet[j].color2, &rate, aet[j].c2_min, aet[j].c2_max);
            Set_Z(&aet[j].z, &rate, aet[j].z_min, aet[j].z_max);
            Set_TexCrd(&aet[j].texcrd, &rate, aet[j].t_min, aet[j].t_max);
        }
    }
}// Paint_Triangle

// 初期化
void M_Create()
{
    // 立方体作成
    Create_Cube();
    // 面・頂点法線ベクトル設定
    Set_Normal();
    // 掛け算対応表作成
    int i,j;
    for(i=0; i<=MULT1; i++){
        for(j=0; j<=MULT2; j++){
            gMultTbl[i][j] = ((i * j) / MULT2);
        }
    }
    // テクスチャ ファイル
    BITMAP      bmp;
    BITMAPINFO  bi;
    char        *p;
    char        path[MAX_PATH];
    GetModuleFileName(NULL, path, sizeof(path));
    p = strrchr(path, '\\');
    strcpy(p+1, "tex2.bmp");
    HBITMAP hBmp = (HBITMAP) LoadImage(NULL, path, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
    GetObject(hBmp, sizeof(bmp), &bmp);
    ZeroMemory(&bi, sizeof(bi));
    bi.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
    bi.bmiHeader.biWidth        = bmp.bmWidth;
    bi.bmiHeader.biHeight       = - bmp.bmHeight;
    bi.bmiHeader.biPlanes       = 1;
    bi.bmiHeader.biBitCount     = 32;
    bi.bmiHeader.biCompression  = BI_RGB;
    gTex.hBmp = (HBITMAP) CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, (VOID**) &gTex.pBits, NULL, 0);
    HDC  hdcCp1,hdcCp2;
    HBITMAP hbmOld1,hbmOld2;
    hdcCp1 = CreateCompatibleDC(NULL);
    hdcCp2 = CreateCompatibleDC(NULL);
    hbmOld1 = (HBITMAP)SelectObject(hdcCp1, gTex.hBmp);
    hbmOld2 = (HBITMAP)SelectObject(hdcCp2, hBmp);
    BitBlt(hdcCp1, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcCp2, 0, 0, SRCCOPY);
    SelectObject(hdcCp1, hbmOld1);
    SelectObject(hdcCp2, hbmOld2);
    DeleteDC(hdcCp1);
    DeleteDC(hdcCp2);
    DeleteObject(hBmp);
    gTex.width  = bmp.bmWidth;
    gTex.height = bmp.bmHeight;
    gTex.stride = WIDTHBYTES(bi.bmiHeader.biWidth * bi.bmiHeader.biBitCount);
    // 24bpp 以下の場合は透明度に 255 を設定
    if(bmp.bmBitsPixel <= 24){
        int size;
        size = gTex.width * gTex.height;
        PBYTE   pb = (PBYTE) gTex.pBits + 3;
        for(i = 0; i < size; i++){
            *pb = MULT2;
            pb += 4;
        }
    }
}// M_Create
	
トップへ 前へ 次へ

 フォグ

遠くのピクセルほどフォグ色に近くする。
考え方は、アルファブレンディングに近い。
上のコードに以下の赤字部分を追加する。
参考資料:Microsoft DirectX9 ヘルプ
typedef struct{
    float   z_near; // 視点に最も近いフォグ Z 位置
    float   z_far;  // 視点から最も遠いフォグ Z 位置
    RGBB    color;  // フォグ色
}FOG,*LPFOG;

FOG         gFog;

void Paint_Triangle(HDC hDC, LPPOLYPAINT pp);
void M_Create();
void M_Paint(HDC hDC);

// 三角形塗り
// pply     : 三角形の頂点と色のリスト
void Paint_Triangle(HDC hDC, LPPOLYPAINT pply)
{
    EDGE        data[3];    // 頂点リスト
    EDGE        aet[2];     // アクティブ辺テーブル(AET)
    ET          et[HEIGHT]; // 辺テーブル(ET)
    int         i,j,y_min,y_max,prev,next,next2,data_ct,aet_ct,y_next;
    UINT        stride_sum;
    float       z = 0;      // Z 値
    ARGBN       argbDiffuse,argbSpecular,argbTex,argbVtx,argbBase;
    VEC2        texcrd;
    RATE        rate;
    
    // Y座標のクリッピング
    for(i=0; i<3; i++){
        if(pply[i].pos.y < 0)
            pply[i].pos.y = 0;
        if(pply[i].pos.y >= HEIGHT)
            pply[i].pos.y = HEIGHT - 1;
    }
    // Y座標の最大値と最小値を求める
    y_min = pply[0].pos.y;
    y_max = pply[0].pos.y;
    for(i=1; i<3; i++){
        if(pply[i].pos.y < y_min){
            y_min = pply[i].pos.y;
        }
        else if(pply[i].pos.y > y_max)
            y_max = pply[i].pos.y;
    }
    // 頂点リスト初期化
    for(i=0; i<3; i++){
        data[i].next = -1;
    }
    // ET 初期化
    for(i=y_min; i<=y_max; i++){
        et[i].index = -1;
        et[i].last = -1;
    }
    data_ct = 0;    // data の要素数
    for(i=0; i<3; i++){
        prev = i - 1;
        if(prev < 0){
            prev = 2;
        }
        next = i + 1;
        if(next >= 3){
            next = 0;
        }
        if(pply[i].pos.y < pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data2(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data2(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y == pply[prev].pos.y){
            Set_Data1(i, i, data_ct, data, pply, et);
        }
    }
    // スキャン・ライン・アルゴリズムによる塗り潰し
    aet_ct = 0; // AET の要素数
    stride_sum = y_min * gnStride;
    for(i=y_min; i<=y_max; i++){
        // スキャンライン上の頂点を検索
        if(et[i].index != -1){
            aet[aet_ct ++] = data[et[i].index];
            next2 = data[et[i].index].next;
            if(next2 != -1){
                aet[aet_ct ++] = data[next2];
                next2 = data[next2].next;
            }
        }
        // AET を x について昇順にソート
        if(aet[0].x > aet[1].x){
            EDGE e = aet[0];
            aet[0] = aet[1];
            aet[1] = e;
        }
        // X座標のクリッピング
        if(aet[0].x < 0){
            aet[0].x = 0;
        }
        if(aet[1].x >= WIDTH){
            aet[1].x = WIDTH - 1;
        }
        // スキャンラインの描画
        LPBYTE  pb = gpBitsWork + stride_sum + aet[0].x * COLOR_BYTE;
        for(j=aet[0].x;j<aet[1].x;j++){
            Set_ValRate(&rate, j, aet[0].x, aet[1].x);
            Set_Z(&z, &rate, aet[0].z, aet[1].z);
            if(z < gZBuf[i][j]){    // ピクセルごとの Z バッファ判定
                gZBuf[i][j] = z;
                // ディフーズ色
                Set_Color(&argbDiffuse, &rate, aet[0].color1, aet[1].color1);
                // スペキュラ色
                Set_Color(&argbSpecular, &rate, aet[0].color2, aet[1].color2);
                if(gSpecularPower > 0){
                    argbVtx = argbSpecular;
                }
                else{
                    argbVtx.a = argbVtx.r = argbVtx.g = argbVtx.b = 0;
                }
                // テクスチャ座標
                Set_TexCrd(&texcrd, &rate, aet[0].texcrd, aet[1].texcrd);
                if(texcrd.x < 0)
                    texcrd.x -= (int)(texcrd.x - 1);
                else if(texcrd.x > 1)
                    texcrd.x -= (int) texcrd.x;
                if(texcrd.y < 0)
                    texcrd.y -= (int)(texcrd.y - 1);
                else if(texcrd.y > 1)
                    texcrd.y -= (int) texcrd.y;
                // テクスチャ色
                int     xTex = (int)(texcrd.x * (gTex.width - 1));
                int     yTex = (int)(texcrd.y * (gTex.height - 1));
                DWORD   val = gTex.pBits[gTex.width * yTex + xTex];
                argbTex.a = (BYTE)(val >> 24);
                argbTex.r = (BYTE)(val >> 16);
                argbTex.g = (BYTE)(val >> 8);
                argbTex.b = (BYTE)(val);
                // テクスチャに濃淡を設定
                argbTex.r = gMultTbl[argbTex.r][argbDiffuse.r];
                argbTex.g = gMultTbl[argbTex.g][argbDiffuse.g];
                argbTex.b = gMultTbl[argbTex.b][argbDiffuse.b];
                // テクスチャにハイライトを設定
                argbVtx.r += argbTex.r;
                argbVtx.g += argbTex.g;
                argbVtx.b += argbTex.b;
                // 環境光設定
                argbVtx.r += gAmbient.r;
                argbVtx.g += gAmbient.g;
                argbVtx.b += gAmbient.b;
                if(argbVtx.r < 0)
                    argbVtx.r = 0;
                else if(argbVtx.r > 255)
                    argbVtx.r = 255;
                if(argbVtx.g < 0)
                    argbVtx.g = 0;
                else if(argbVtx.g > 255)
                    argbVtx.g = 255;
                if(argbVtx.b < 0)
                    argbVtx.b = 0;
                else if(argbVtx.b > 255)
                    argbVtx.b = 255;
                // フォグ
                int n1;
                if(gFog.z_far != gFog.z_near)
                    n1 = (int)((gFog.z_far - z) * MULT2 / (gFog.z_far - gFog.z_near));
                else
                    n1 = 1;
                if(n1 < 0)
                    n1 = -n1;
                else if(n1 > MULT2)
                    n1 = MULT2;
                int n2 = MULT2 - n1;
                argbVtx.r = (BYTE)(gMultTbl[argbVtx.r][n1] + gMultTbl[gFog.color.r][n2]);
                argbVtx.g = (BYTE)(gMultTbl[argbVtx.g][n1] + gMultTbl[gFog.color.g][n2]);
                argbVtx.b = (BYTE)(gMultTbl[argbVtx.b][n1] + gMultTbl[gFog.color.b][n2]);
                // アルファ ブレンディング
                argbVtx.a = gMultTbl[argbVtx.a][argbTex.a];
                argbBase.a = MULT2 - argbVtx.a;
                argbBase.b = *(pb + 0);
                argbBase.g = *(pb + 1);
                argbBase.r = *(pb + 2);
                argbVtx.r = gMultTbl[argbVtx.r][argbVtx.a] + gMultTbl[argbBase.r][argbBase.a];
                argbVtx.g = gMultTbl[argbVtx.g][argbVtx.a] + gMultTbl[argbBase.g][argbBase.a];
                argbVtx.b = gMultTbl[argbVtx.b][argbVtx.a] + gMultTbl[argbBase.b][argbBase.a];
                // ピクセル値設定
                *pb ++ = (BYTE) argbVtx.b;
                *pb ++ = (BYTE) argbVtx.g;
                *pb ++ = (BYTE) argbVtx.r;
            }
            else{
                pb += COLOR_BYTE;
            }
        }
        stride_sum += gnStride;
        // i == y_max の要素を AET から削除
        if(i == aet[1].y_max){
            aet_ct --;
        }
        if(i == aet[0].y_max){
            aet[0] = aet[1];
            aet_ct --;
        }
        // AET の各 x を次のスキャンラインでの値に更新
        for(j=0;j<2;j++){
            y_next = i + 1;
            aet[j].x = Round(aet[j].x_min + aet[j].slant_sum);
            aet[j].slant_sum += aet[j].slant;
            Set_ValRate(&rate, y_next, aet[j].y_min, aet[j].y_max);
            Set_Color(&aet[j].color1, &rate, aet[j].c1_min, aet[j].c1_max);
            Set_Color(&aet[j].color2, &rate, aet[j].c2_min, aet[j].c2_max);
            Set_Z(&aet[j].z, &rate, aet[j].z_min, aet[j].z_max);
            Set_TexCrd(&aet[j].texcrd, &rate, aet[j].t_min, aet[j].t_max);
        }
    }
}// Paint_Triangle

// 初期化
void M_Create()
{
    // 立方体作成
    Create_Cube();
    // 面・頂点法線ベクトル設定
    Set_Normal();
    // 掛け算対応表作成
    int i,j;
    for(i=0; i<=MULT1; i++){
        for(j=0; j<=MULT2; j++){
            gMultTbl[i][j] = ((i * j) / MULT2);
        }
    }
    // テクスチャ ファイル
    BITMAP      bmp;
    BITMAPINFO  bi;
    char        *p;
    char        path[MAX_PATH];
    GetModuleFileName(NULL, path, sizeof(path));
    p = strrchr(path, '\\');
    strcpy(p+1, "tex2.bmp");
    HBITMAP hBmp = (HBITMAP) LoadImage(NULL, path, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION);
    GetObject(hBmp, sizeof(bmp), &bmp);
    ZeroMemory(&bi, sizeof(bi));
    bi.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
    bi.bmiHeader.biWidth        = bmp.bmWidth;
    bi.bmiHeader.biHeight       = - bmp.bmHeight;
    bi.bmiHeader.biPlanes       = 1;
    bi.bmiHeader.biBitCount     = 32;
    bi.bmiHeader.biCompression  = BI_RGB;
    gTex.hBmp = (HBITMAP) CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, (VOID**) &gTex.pBits, NULL, 0);
    HDC  hdcCp1,hdcCp2;
    HBITMAP hbmOld1,hbmOld2;
    hdcCp1 = CreateCompatibleDC(NULL);
    hdcCp2 = CreateCompatibleDC(NULL);
    hbmOld1 = (HBITMAP)SelectObject(hdcCp1, gTex.hBmp);
    hbmOld2 = (HBITMAP)SelectObject(hdcCp2, hBmp);
    BitBlt(hdcCp1, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcCp2, 0, 0, SRCCOPY);
    SelectObject(hdcCp1, hbmOld1);
    SelectObject(hdcCp2, hbmOld2);
    DeleteDC(hdcCp1);
    DeleteDC(hdcCp2);
    DeleteObject(hBmp);
    gTex.width  = bmp.bmWidth;
    gTex.height = bmp.bmHeight;
    gTex.stride = WIDTHBYTES(bi.bmiHeader.biWidth * bi.bmiHeader.biBitCount);
    // 24bpp 以下の場合は透明度に 255 を設定
    if(bmp.bmBitsPixel <= 24){
        int size;
        size = gTex.width * gTex.height;
        PBYTE   pb = (PBYTE) gTex.pBits + 3;
        for(i = 0; i < size; i++){
            *pb = MULT2;
            pb += 4;
        }
    }
    // フォグ情報設定
    gFog.z_near = gZFar - 0.4f;
    gFog.z_far  = gZFar;
    gFog.color.r = gFog.color.g = gFog.color.b = 128;
}// M_Create

// 描画
void M_Paint(HDC hDC)
{
    const VEC3  pos_look = {0.0f, 0.0f, -2.0f};     // 視点の位置
    const VEC3  pos_lookat = {0.0f, 0.0f, 0.0f};    // 注視点の位置
    const VEC3  v_lookup = {0, 1, 0};               // 視点の上向き
    VEC3        v_sun = {0.0f, -1.0f, 1.0f};        // 入射光のベクトル
    const RGBB  material = {255, 255, 255};         // 面のディフューズ色(各色 0 〜 255)
    const RGBB  ambient = {10, 10, 10};             // 環境光(各色 0 〜 255)
    MTRX        mtScal,mtRotY,mtTrans,mtWork;
    static int  roll_y = 0;                         // 回転角度
    
    // 行列設定
    gMtView     = MtrxView(pos_look, pos_lookat, v_lookup);
    gMtProj     = MtrxPerspect(0.0f, 10.0f, 3.14f / 4, 1.0f);
//  gMtProj     = MtrxOrtho(0.0f, 10.0f, 5.0f, 5.0f);
    // 入射光ベクトル
    v_sun.x     = - v_sun.x;
    v_sun.y     = - v_sun.y;
    v_sun.z     = - v_sun.z;
    v_sun       = Vec3Normalize(v_sun);
    // Zバッファ初期化
    Clear_ZBuf();
    // オブジェクト描画
    mtScal      = MtrxScal(1.0f, 1.0f, 1.0f);
    mtRotY      = MtrxRotY(roll_y * 3.14f / 180);
    mtWork      = MtrxMult(mtScal, mtRotY);
    mtTrans     = MtrxTranslate(-0.2f, 0.2f, 0.0f);
    gMtWorld    = MtrxMult(mtWork, mtTrans);
    Draw_Cube(hDC, v_sun);
    mtTrans     = MtrxTranslate(0.2f, 0.0f,-1.0f);
    gMtWorld    = MtrxMult(mtWork, mtTrans);
    Draw_Cube(hDC, v_sun);
    // 回転角度増
    roll_y += 10;
    if(roll_y >= 360)
        roll_y = 0;
}// M_Paint
	
トップへ 前へ 次へ

 ピクセル単位のライティング

これまでは、面単位のライティングであるグーローシェーディングを使ってきたが、
ピクセル単位のライティングに変更すると、少ないポリゴン数でも多くのポリゴンを
使ったような高品質のレンダリング結果が得られる。
ただし、ライティング計算回数が増えるため、オーバーヘッドが高くなる。
テクスチャ表示のコードに以下の赤字部分を置換または追加する。
参考資料:コンピュータ・グラフィックス J.D.FOLEY/A.VAN DAM著 日本コンピュータ協会
typedef struct{
    BYTE    a;
    BYTE    r;
    BYTE    g;
    BYTE    b;
}ARGBB,*LPARGBB;

typedef struct{
    POINT       pos;    // 頂点座標
    VEC3        n;      // 頂点法線ベクトル
    float       z;      // 頂点Z値
    D3DXVECTOR2 t;      // テクスチャ座標
}POLYPAINT,*LPPOLYPAINT;

typedef struct{
    int         y_min;  // 辺の最小Y座標
    int         y_max;  // 辺の最大Y座標
    int         x_min;  // 辺の最小Y座標のX座標
    int         x;      // スキャンラインと辺の交点のX座標
    double      slant;  // 辺の傾きの逆数の整数部
    double      slant_sum;  // slantの和
    VEC3        n_min;  // 辺の最小Y座標の頂点法線ベクトル
    VEC3        n_max;  // 辺の最大Y座標の頂点法線ベクトル
    VEC3        normal; // 頂点法線ベクトル
    float       z_min;  // 辺の最小Y座標のZ値
    float       z_max;  // 辺の最大Y座標のZ値
    float       z;      // Z値
    D3DXVECTOR2 t_min;  // 辺の最小Y座標のテクスチャ座標
    D3DXVECTOR2 t_max;  // 辺の最大Y座標のテクスチャ座標
    D3DXVECTOR2 texcrd; // テクスチャ座標
    int         next;   // 次のデータ番号
}EDGE,*LPEDGE;

float   d_perspect = HEIGHT;                // 透視投象での投影するスクリーンと視点の距離
POINT   offset = {WIDTH / 2, HEIGHT / 2};   // 投象後の二次元座標の平行移動幅
float   power1 = 0.7f;                      // 反射の強さ(0 〜 1)
int     specular_power = 3;                 // スペキュラ反射の強さ
ARGBB   diffuse = {128, 255, 255, 255};     // 面のディフューズ色(各色 0 〜 255)
ARGBB   specular = {255, 255, 255, 255};    // 面のスペキュラ色(各色 0 〜 255)
RGBB    ambient = {10, 10, 10};             // 環境光(各色 0 〜 255)

void Draw_Cube(HDC hDC, VEC3 v_sun);
void Paint_Triangle(HDC hDC, LPPOLYPAINT pp, VEC3 v_sun);
void Set_Data1(int idx_min, int idx_max, int &data_ct, LPEDGE data, LPPOLYPAINT pply, LPET et);
void Set_Data2(int idx_min, int idx_max, int &data_ct, LPEDGE data, LPPOLYPAINT pply, LPET et);
void Set_Vector(LPVEC3 vector, LPRATE rate, VEC3 v_min, VEC3 v_max);
void M_Paint(HDC hDC);

// 立方体描画
void Draw_Cube(HDC hDC, VEC3 v_sun)
{
    int         i,j;
    VEC3        v_wv[4],v_all[4];
    VEC3        v_look;                             // 注視点から視点へのベクトル
    POLYPAINT   ply1[4];                            // 4頂点ポリゴン色塗り情報
    POLYPAINT   ply2[2][3];                         // 3頂点ポリゴン色塗り情報
    
    gMtWorldView    = MtrxMult(gMtWorld, gMtView);
    gMtAll          = MtrxMult(gMtWorldView, gMtProj);
    
    // 視線ベクトル
    VEC3    posLookAt = MtrxVec3(gPosLookAt, gMtWorld);
    v_look  = Vec3Normalize(MtrxVec3(Vec3Sub(posLookAt, gPosLook), gMtView));
    
    for(i=0; i<6; i++){
        for(j=0; j<4; j++){
            int  index = gmCube.f[i][j];
            // 頂点のワールド・ビュー行列変換
            v_wv[j] = Vec3Normalize(MtrxVec3(gmCube.v[index], gMtWorldView));
        }
        // 面法線ベクトル計算(カリング用)
        VEC3    fn_cull = Get_FaceNormal(v_wv);
        // カリング
        float   dot_cull = Vec3Dot(v_look, fn_cull);
        if(dot_cull < 0){   // 片面表示
            for(j=0; j<4; j++){
                int  index = gmCube.f[i][j];
                // 頂点のワールド・ビュー・プロジェクション行列変換
                v_all[j] = MtrxVec3(gmCube.v[index], gMtAll);
                // 頂点法線ベクトルのワールド行列変換
                VEC3    n_color;
                n_color = MtrxVec3(gmCube.n[index], gMtWorld);
                n_color = Vec3Normalize(n_color);
                float size_rate = d_perspect;
                if(gFlagPerspect)
                    size_rate /= (v_all[j].z + gPosLookAt.z - gPosLook.z);
                ply1[j].pos.x   = (long)( v_all[j].x * size_rate + offset.x);
                ply1[j].pos.y   = (long)(-v_all[j].y * size_rate + offset.y);
                ply1[j].z       = v_all[j].z;
                ply1[j].n       = n_color;
                ply1[j].t       = gmCube.t[index];
            }
            ply2[0][0] = ply1[0];
            ply2[0][1] = ply1[1];
            ply2[0][2] = ply1[2];
            ply2[1][0] = ply1[0];
            ply2[1][1] = ply1[2];
            ply2[1][2] = ply1[3];
            Paint_Triangle(hDC, ply2[0], v_sun);
            Paint_Triangle(hDC, ply2[1], v_sun);
        }
    }
}// Draw_Cube

// 三角形塗り
// pply     : 三角形の頂点と色のリスト
void Paint_Triangle(HDC hDC, LPPOLYPAINT pply, VEC3 v_sun)
{
    EDGE        data[3];    // 頂点リスト
    EDGE        aet[2];     // アクティブ辺テーブル(AET)
    ET          et[HEIGHT]; // 辺テーブル(ET)
    int         i,j,y_min,y_max,prev,next,next2,data_ct,aet_ct,y_next;
    UINT        stride_sum;
    float       z = 0;      // Z 値
    ARGBN       argbLight,argbTex,argbVtx,argbBase;
    VEC2        texcrd;
    VEC3        normal;
    RATE        rate;
    float       power2,powerSpecular,powerDiffuse;
    
    // Y座標のクリッピング
    for(i=0; i<3; i++){
        if(pply[i].pos.y < 0)
            pply[i].pos.y = 0;
        if(pply[i].pos.y >= HEIGHT)
            pply[i].pos.y = HEIGHT - 1;
    }
    // Y座標の最大値と最小値を求める
    y_min = pply[0].pos.y;
    y_max = pply[0].pos.y;
    for(i=1; i<3; i++){
        if(pply[i].pos.y < y_min){
            y_min = pply[i].pos.y;
        }
        else if(pply[i].pos.y > y_max)
            y_max = pply[i].pos.y;
    }
    // 頂点リスト初期化
    for(i=0; i<3; i++){
        data[i].next = -1;
    }
    // ET 初期化
    for(i=y_min; i<=y_max; i++){
        et[i].index = -1;
        et[i].last = -1;
    }
    data_ct = 0;    // data の要素数
    for(i=0; i<3; i++){
        prev = i - 1;
        if(prev < 0){
            prev = 2;
        }
        next = i + 1;
        if(next >= 3){
            next = 0;
        }
        if(pply[i].pos.y < pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data2(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y > pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data2(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[prev].pos.y && pply[i].pos.y < pply[next].pos.y){
            Set_Data1(i, next, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y < pply[prev].pos.y){
            Set_Data1(i, prev, data_ct, data, pply, et);
        }
        else if(pply[i].pos.y == pply[next].pos.y && pply[i].pos.y == pply[prev].pos.y){
            Set_Data1(i, i, data_ct, data, pply, et);
        }
    }
    // スキャン・ライン・アルゴリズムによる塗り潰し
    aet_ct = 0; // AET の要素数
    stride_sum = y_min * gnStride;
    for(i=y_min; i<=y_max; i++){
        // スキャンライン上の頂点を検索
        if(et[i].index != -1){
            aet[aet_ct ++] = data[et[i].index];
            next2 = data[et[i].index].next;
            if(next2 != -1){
                aet[aet_ct ++] = data[next2];
                next2 = data[next2].next;
            }
        }
        // AET を x について昇順にソート
        if(aet[0].x > aet[1].x){
            EDGE e = aet[0];
            aet[0] = aet[1];
            aet[1] = e;
        }
        // X座標のクリッピング
        if(aet[0].x < 0){
            aet[0].x = 0;
        }
        if(aet[1].x >= WIDTH){
            aet[1].x = WIDTH - 1;
        }
        // スキャンラインの描画
        LPBYTE  pb = gpBitsWork + stride_sum + aet[0].x * COLOR_BYTE;
        for(j=aet[0].x;j<aet[1].x;j++){
            Set_ValRate(&rate, j, aet[0].x, aet[1].x);
            Set_Z(&z, &rate, aet[0].z, aet[1].z);
            if(z < gZBuf[i][j]){    // ピクセルごとの Z バッファ判定
                gZBuf[i][j] = z;
                // 照明色
                Set_Vector(&normal, &rate, aet[0].normal, aet[1].normal);
                power2 = Vec3Dot(v_sun, normal);
                powerDiffuse = power1 * power2;
                if(powerDiffuse < 0){
                    powerDiffuse = 0;
                }
                argbLight.a = diffuse.a;
                argbLight.r = (UINT)(diffuse.r * powerDiffuse);
                argbLight.g = (UINT)(diffuse.g * powerDiffuse);
                argbLight.b = (UINT)(diffuse.b * powerDiffuse);
                if(argbLight.r > 255)
                    argbLight.r = 255;
                if(argbLight.g > 255)
                    argbLight.g = 255;
                if(argbLight.b > 255)
                    argbLight.b = 255;
                if(specular_power > 0){
                    powerSpecular = power1 * (float) pow(power2, (int) specular_power);
                    if(powerSpecular < 0){
                        powerSpecular = 0;
                    }
                    argbVtx.a = diffuse.a;
                    argbVtx.r = (UINT)(specular.r * powerSpecular);
                    argbVtx.g = (UINT)(specular.g * powerSpecular);
                    argbVtx.b = (UINT)(specular.b * powerSpecular);
                    if(argbVtx.r > 255)
                        argbVtx.r = 255;
                    if(argbVtx.g > 255)
                        argbVtx.g = 255;
                    if(argbVtx.b > 255)
                        argbVtx.b = 255;
                }
                // テクスチャ座標
                Set_TexCrd(&texcrd, &rate, aet[0].texcrd, aet[1].texcrd);
                if(texcrd.x < 0)
                    texcrd.x -= (int)(texcrd.x - 1);
                else if(texcrd.x > 1)
                    texcrd.x -= (int) texcrd.x;
                if(texcrd.y < 0)
                    texcrd.y -= (int)(texcrd.y - 1);
                else if(texcrd.y > 1)
                    texcrd.y -= (int) texcrd.y;
                // テクスチャ色
                int     xTex = (int)(texcrd.x * (gTex.width - 1));
                int     yTex = (int)(texcrd.y * (gTex.height - 1));
                DWORD   val = gTex.pBits[gTex.width * yTex + xTex];
                argbTex.r = (BYTE)(val >> 16);
                argbTex.g = (BYTE)(val >> 8);
                argbTex.b = (BYTE)(val);
                // テクスチャに濃淡を設定
                argbTex.r = gMultTbl[argbTex.r][argbLight.r];
                argbTex.g = gMultTbl[argbTex.g][argbLight.g];
                argbTex.b = gMultTbl[argbTex.b][argbLight.b];
                // テクスチャにハイライトを設定
                argbVtx.r += argbTex.r;
                argbVtx.g += argbTex.g;
                argbVtx.b += argbTex.b;
                // 環境光設定
                argbVtx.r += gAmbient.r;
                argbVtx.g += gAmbient.g;
                argbVtx.b += gAmbient.b;
                if(argbVtx.r < 0)
                    argbVtx.r = 0;
                else if(argbVtx.r > 255)
                    argbVtx.r = 255;
                if(argbVtx.g < 0)
                    argbVtx.g = 0;
                else if(argbVtx.g > 255)
                    argbVtx.g = 255;
                if(argbVtx.b < 0)
                    argbVtx.b = 0;
                else if(argbVtx.b > 255)
                    argbVtx.b = 255;
                // ピクセル値設定
                *pb ++ = (BYTE) argbVtx.b;
                *pb ++ = (BYTE) argbVtx.g;
                *pb ++ = (BYTE) argbVtx.r;
            }
            else{
                pb += COLOR_BYTE;
            }
        }
        stride_sum += gnStride;
        // i == y_max の要素を AET から削除
        if(i == aet[1].y_max){
            aet_ct --;
        }
        if(i == aet[0].y_max){
            aet[0] = aet[1];
            aet_ct --;
        }
        // AET の各 x を次のスキャンラインでの値に更新
        for(j=0;j<2;j++){
            y_next = i + 1;
            aet[j].x = Round(aet[j].x_min + aet[j].slant_sum);
            aet[j].slant_sum += aet[j].slant;
            Set_ValRate(&rate, y_next, aet[j].y_min, aet[j].y_max);
            Set_Vector(&aet[j].normal, &rate, aet[j].n_min, aet[j].n_max);
            Set_Z(&aet[j].z, &rate, aet[j].z_min, aet[j].z_max);
            Set_TexCrd(&aet[j].texcrd, &rate, aet[j].t_min, aet[j].t_max);
        }
    }
}// Paint_Triangle

// 頂点リスト設定1
void Set_Data1(int idx_min, int idx_max, int &data_ct, LPEDGE data, LPPOLYPAINT pply, LPET et)
{
    int et_idx = pply[idx_min].pos.y;
    if(et[et_idx].last == -1)
        et[et_idx].index = data_ct;
    else
        data[et[et_idx].last].next = data_ct;
    et[et_idx].last = data_ct;
    
    data[data_ct].y_min = pply[idx_min].pos.y;
    data[data_ct].y_max = pply[idx_max].pos.y;
    data[data_ct].x_min = pply[idx_min].pos.x;
    data[data_ct].x     = data[data_ct].x_min;
    data[data_ct].slant = (double)(pply[idx_max].pos.x - pply[idx_min].pos.x) / (pply[idx_max].pos.y - pply[idx_min].pos.y);
    data[data_ct].slant_sum = data[data_ct].slant;
    data[data_ct].n_min = pply[idx_min].n;
    data[data_ct].n_max = pply[idx_max].n;
    data[data_ct].normal = data[data_ct].n_min;
    data[data_ct].z_min  = pply[idx_min].z;
    data[data_ct].z_max  = pply[idx_max].z;
    data[data_ct].z      = data[data_ct].z_min;
    data[data_ct].t_min  = pply[idx_min].t;
    data[data_ct].t_max  = pply[idx_max].t;
    data[data_ct].texcrd = data[data_ct].t_min;
    data_ct ++;
}// Set_Data1

// 頂点リスト設定2
void Set_Data2(int idx_min, int idx_max, int &data_ct, LPEDGE data, LPPOLYPAINT pply, LPET et)
{
    int et_idx = pply[idx_min].pos.y + 1;
    if(et[et_idx].last == -1)
        et[et_idx].index = data_ct;
    else
        data[et[et_idx].last].next = data_ct;
    et[et_idx].last = data_ct;
    
    double  slant = (double)(pply[idx_max].pos.x - pply[idx_min].pos.x) / (pply[idx_max].pos.y - pply[idx_min].pos.y);
    RATE    rate;
    VEC2    texcrd;
    float   z;
    VEC3    normal;
    Set_ValRate(&rate, et_idx, pply[idx_min].pos.y, pply[idx_max].pos.y);
    data[data_ct].y_min = pply[idx_min].pos.y;
    data[data_ct].y_max = pply[idx_max].pos.y;
    data[data_ct].x_min = pply[idx_min].pos.x;
    data[data_ct].x     = Round(data[data_ct].x_min + slant);
    data[data_ct].slant = slant;
    data[data_ct].slant_sum = data[data_ct].slant + slant;
    Set_Vector(&normal, &rate, pply[idx_min].n, pply[idx_max].n);
    data[data_ct].n_min = pply[idx_min].n;
    data[data_ct].n_max = pply[idx_max].n;
    data[data_ct].normal = normal;
    Set_Z(&z, &rate, pply[idx_min].z, pply[idx_max].z);
    data[data_ct].z_min = pply[idx_min].z;
    data[data_ct].z_max = pply[idx_max].z;
    data[data_ct].z     = z;
    Set_TexCrd(&texcrd, &rate, pply[idx_min].t, pply[idx_max].t);
    data[data_ct].t_min = pply[idx_min].t;
    data[data_ct].t_max = pply[idx_max].t;
    data[data_ct].texcrd = texcrd;
    data_ct ++;
}// Set_Data2

// ベクトル設定
void Set_Vector(LPVEC3 vector, LPRATE rate, VEC3 v_min, VEC3 v_max)
{
    if(rate->f1 != -1){
        vector->x = v_max.x * rate->f1 + v_min.x * rate->f2;
        vector->y = v_max.y * rate->f1 + v_min.y * rate->f2;
        vector->z = v_max.z * rate->f1 + v_min.z * rate->f2;
    }
    else{
        *vector = v_min;
    }
}// Set_Vector

// 描画
void M_Paint(HDC hDC)
{
    const VEC3  pos_look = {0.0f, 2.0f, -2.0f};     // 視点の位置
    const VEC3  pos_lookat = {0.0f, 0.0f, 0.0f};    // 注視点の位置
    const VEC3  v_lookup = {0, 1, 0};               // 視点の上向き
    VEC3        v_sun = {0.0f, -1.0f, 1.0f};        // 入射光のベクトル
    MTRX        mtScal,mtRotY,mtTrans,mtWork;
    static int  roll_y = 0;                         // 回転角度
    
    // 行列設定
    gMtView     = MtrxView(pos_look, pos_lookat, v_lookup);
    gMtProj     = MtrxPerspect(0.0f, 10.0f, 3.14f / 4, 1.0f);
//  gMtProj     = MtrxOrtho(0.0f, 10.0f, 5.0f, 5.0f);
    // 入射光ベクトル
    v_sun.x     = - v_sun.x;
    v_sun.y     = - v_sun.y;
    v_sun.z     = - v_sun.z;
    v_sun       = Vec3Normalize(v_sun);
    // Zバッファ初期化
    Clear_ZBuf();
    // オブジェクト描画
    mtScal      = MtrxScal(1.0f, 1.0f, 1.0f);
    mtRotY      = MtrxRotY(roll_y * 3.14f / 180);
    mtWork      = MtrxMult(mtScal, mtRotY);
    mtTrans     = MtrxTranslate(-0.2f, 0.2f, 0.0f);
    gMtWorld    = MtrxMult(mtWork, mtTrans);
    Draw_Cube(hDC, v_sun);
    mtTrans     = MtrxTranslate(0.2f, 0.0f,-0.2f);
    gMtWorld    = MtrxMult(mtWork, mtTrans);
    Draw_Cube(hDC, v_sun);
    // 回転角度増
    roll_y += 10;
    if(roll_y >= 360)
        roll_y = 0;
}// M_Paint
	
トップへ 前へ 次へ

 



	
トップへ 前へ 次へ

 



	
トップへ 前へ 次へ

 



	
トップへ 前へ 次へ

 

 
	
トップへ 前へ 次へ