3DCGの理屈について(cとWINAPIで書きます)

記事
IT・テクノロジー
3DCGというと、ポリゴンというイメージが強いですが、
レイトレーシングというかなりリアルに見える3DCG技術も
近年発達しています。

バイオハザードのRE2とかのリメイクなんかはひょっとするとレイトレーシングを使ってたりするのではないかと思ってますが、ユーチューブでゲーム実況動画を観たらあまりにも内蔵やらなにやらがグロいゾンビの死体の一部だったので気持ち悪くなるほどでした。

さて、ここでは、点、ワイヤーフレーム、フラットシェーディング、グーローシェーディング、フォンシェーディングについて書きます。

しかし、最近の言語で高速に点描画が出来るプラットフォームを知らないのと
c+winapiの組み合わせがやはりスクリプト言語より速い(気がする)します。なので、とりあえずcで組んでみます。実はc以降に作られた言語には
色々とc言語と似通った部分があるのでcを覚えるのは役に立つのでは
ないでしょうか?

コンパイルの仕方(cをexeに変換するやり方)はVisualC++ExpressEditionでも可能です。

ソースをmain.cppにして、Ctrl+F5でコンパイルできます。
文字セットをユニコードではなく、マルチバイトにしておきます。

001:点を打つ
001点を打つ.png
プログラムはだんちょう、じゃなくて冗長ですが、cとしては短い方だと
思います。
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>


#define WIDTH 320
#define HEIGHT 240

int min_[HEIGHT];
int max_[HEIGHT];

#define SCREENHEIGHT HEIGHT

#define SCALE 120.0f

int MAXVTX=0;//最大の頂点数
int MAXPOL=0;//最大のポリゴン数
int LastLine=0;

BITMAPINFO biInfo;
LPDWORD lpPixel;
struct TClipSize
{
 int width,height;
};

struct TClipBltInfo
{
 int sw,sh;
 int sx,sy;
 int dx,dy;
};
HBITMAP hBMP, hBMPOLD;
HDC hdcBMP;
HDC HdcGlobal;
char FileStr[10000][800];
inline void PSET(int x, int y, int col);//点を打つ関数
int FPSCount=0;
void CalcFPS(HDC hdc)
{
 char str[100];
 static int FPS=0;
 static long int time=GetTickCount();
 //FPSCount++;
 if(GetTickCount()-time>1000)
 {
  time=GetTickCount();
  FPS=FPSCount;
  FPSCount=0;
 }else{
  FPSCount++;
 }
 sprintf(str,"FPS=%d",FPS);
 TextOut(hdc,0,0,str,strlen(str));
 //TextOut(hdc,0,90,"PRESS SPACE KEY!!",strlen("PRESS SPACE KEY!!"));
}
BOOL SetClientSize(HWND hWnd, int width, int height)
{
 RECT rw, rc;
 ::GetWindowRect(hWnd, &rw);
 ::GetClientRect(hWnd, &rc);
 int new_width = (rw.right - rw.left) - (rc.right - rc.left) + width;
 int new_height = (rw.bottom - rw.top) - (rc.bottom - rc.top) + height;
 return ::SetWindowPos(hWnd, NULL, 0, 0, new_width, new_height, SWP_NOMOVE | SWP_NOZORDER);
}
inline void ClearScreen()
{
 for(int i=0;i<480;i++)
 {
  for(int j=0;j<640;j++)
  {
   PSET(j,i,0x00000000);
  }
 }
}
inline void PSET(int x, int y, int col)
{
 int R = (col >> 16) & 0xFF;
 int G = (col >> 8) & 0xFF;
 int B = col & 0xFF;
 int address;
 //点を打つ
 if (x >= 0 && y >= 0 && x < WIDTH && y < HEIGHT)
 {
  address = x + (y * WIDTH);
  lpPixel[address] = RGB(B,G,R);
 }
}
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE hInst;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow){
 HWND hwMain;
 MSG msg;
 WNDCLASS wndclass;
 hInst = hInstance;
 wndclass.style = CS_HREDRAW | CS_VREDRAW;
 wndclass.lpfnWndProc = WndProc;
 wndclass.cbClsExtra = 0;
 wndclass.cbWndExtra = 0;
 wndclass.hInstance = hInstance;
 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 wndclass.lpszMenuName = NULL;
 wndclass.lpszClassName = "CWindow";
 RegisterClass(&wndclass);
 hwMain = CreateWindow("CWindow", "Phong Shading(is Testing)",
          WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
          WIDTH*2+22,HEIGHT*2+36+6, NULL, NULL, hInstance, NULL);
    SetClientSize(hwMain,320*2,240*2);//ウィンドウサイズ丁度良いサイズに調整してくれる
 /* ウインドウを表示 */
 ShowWindow(hwMain, iCmdShow);
 UpdateWindow(hwMain);
 /* メッセージループ */
 while(GetMessage(&msg, NULL, 0, 0)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
 return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
    HDC hdc;
    PAINTSTRUCT ps;
 static int DrawMode = 0;
 static int prevSpace = 0;
    switch (iMsg) {
    case WM_TIMER:
        {
            if(GetAsyncKeyState(VK_ESCAPE)<0)
                SendMessage(hwnd,WM_DESTROY,NULL,NULL);
            InvalidateRect(hwnd,NULL,NULL);
        }
        return 0;
    case WM_CREATE:
        SetTimer(hwnd,100,1000/900,NULL);
        /* BITMAPINFOをゼロクリア */
        ZeroMemory(&biInfo, sizeof(BITMAPINFO));
        /* BITMAPINFO設定 */
        biInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        biInfo.bmiHeader.biWidth = WIDTH;
        biInfo.bmiHeader.biHeight = -HEIGHT;
        biInfo.bmiHeader.biPlanes = 1;
        biInfo.bmiHeader.biBitCount = 32;
        biInfo.bmiHeader.biCompression = BI_RGB;
        /* ウインドウのDCを取得 */
        hdc = GetDC(hwnd);
        /* biInfoの形式でDIBSectionを作成 */
        hBMP =CreateDIBSection(hdc, &biInfo, DIB_RGB_COLORS, (LPVOID*)(&lpPixel), NULL, 0);
        /* DIBSection用のメモリDCを作成 */
        hdcBMP = CreateCompatibleDC(hdc);
        /* メモリDCにDIBSectionを選択 */
        hBMPOLD = (HBITMAP)SelectObject(hdcBMP, hBMP);
        /* 不要になったウインドウのDCを解放 */
        ReleaseDC(hwnd, hdc);
        return 0;
    case WM_PAINT:
        {
  int i;
        hdc = BeginPaint(hwnd, &ps);
        ClearScreen();//画面を消す
        PSET(160,120,0x00ff0000);//点を打つ
        /* DIBSectionをDIBとして描画 */
            StretchDIBits(hdc, 0, 0, WIDTH*2, HEIGHT*2,
                0, 0, WIDTH, HEIGHT, lpPixel, &biInfo,
                DIB_RGB_COLORS,SRCCOPY);
            CalcFPS(hdc);
            EndPaint(hwnd, &ps);
            DeleteObject(SelectObject(hdc , GetStockObject(WHITE_BRUSH)));
        }
        return 0;
    case WM_DESTROY: /* ウインドウ破棄時 */
        SelectObject(hdcBMP, hBMPOLD);
        DeleteObject(hdcBMP);
        DeleteObject(hBMP);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, iMsg, wParam, lParam);
}

002:線を描く
点の連続が線、線の連続が三角形となるのは
なんとなく分かるかもしれません。
点、線、三角形.png
つまり、ソフトウェアレンダリングで三角ポリゴンを描く為には
直線プログラムを書くのは宿命だとも言えます。

とここで、とある方から教わった公式があります。
Aは0.00から1.00に変化させる前提ですが、
変化中=変化前*(1-Α)+変化後*A

これをXに変形すると、
Cx=X1*(1-d)+X2*d
さらにYについて追加すると
Cy=Y1*(1-d)+Y2*d
となります。
これらの公式で直線が描けます。
線を描く.png

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define WIDTH 320
#define HEIGHT 240
int min_[HEIGHT];
int max_[HEIGHT];
#define SCREENHEIGHT HEIGHT
#define SCALE 120.0f
int MAXVTX=0;//最大の頂点数
int MAXPOL=0;//最大のポリゴン数
int LastLine=0;
BITMAPINFO biInfo;
LPDWORD lpPixel;
struct TClipSize
{
 int width,height;
};
struct TClipBltInfo
{
 int sw,sh;
 int sx,sy;
 int dx,dy;
};
HBITMAP hBMP, hBMPOLD;
HDC hdcBMP;
HDC HdcGlobal;
char FileStr[10000][800];
inline void PSET(int x, int y, int col);//点を打つ関数
int FPSCount=0;
void CalcFPS(HDC hdc)
{
 char str[100];
 static int FPS=0;
 static long int time=GetTickCount();
 //FPSCount++;
 if(GetTickCount()-time>1000)
 {
  time=GetTickCount();
  FPS=FPSCount;
  FPSCount=0;
 }else{
  FPSCount++;
 }
 sprintf(str,"FPS=%d",FPS);
 TextOut(hdc,0,0,str,strlen(str));
 //TextOut(hdc,0,90,"PRESS SPACE KEY!!",strlen("PRESS SPACE KEY!!"));
}
BOOL SetClientSize(HWND hWnd, int width, int height)
{
 RECT rw, rc;
 ::GetWindowRect(hWnd, &rw);
 ::GetClientRect(hWnd, &rc);
 int new_width = (rw.right - rw.left) - (rc.right - rc.left) + width;
 int new_height = (rw.bottom - rw.top) - (rc.bottom - rc.top) + height;
 return ::SetWindowPos(hWnd, NULL, 0, 0, new_width, new_height, SWP_NOMOVE | SWP_NOZORDER);
}
inline void ClearScreen()
{
 for(int i=0;i<480;i++)
 {
  for(int j=0;j<640;j++)
  {
   PSET(j,i,0x00000000);
  }
 }
}
inline void PSET(int x, int y, int col)
{
 int R = (col >> 16) & 0xFF;
 int G = (col >> 8) & 0xFF;
 int B = col & 0xFF;
 int address;
 //点を打つ
 if (x >= 0 && y >= 0 && x < WIDTH && y < HEIGHT)
 {
  address = x + (y * WIDTH);
  lpPixel[address] = RGB(B,G,R);
 }
}
inline void LINE(int x1,int y1,int x2,int y2,int col)
{
    //線分を描画する
    for(int i=0;i<=256;i++)
    {
        int ax=x1*(256-i)+x2*i>>8;
        int ay=y1*(256-i)+y2*i>>8;
        PSET(ax,ay,col);
    }
}
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE hInst;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow){
 HWND hwMain;
 MSG msg;
 WNDCLASS wndclass;
 hInst = hInstance;
 wndclass.style = CS_HREDRAW | CS_VREDRAW;
 wndclass.lpfnWndProc = WndProc;
 wndclass.cbClsExtra = 0;
 wndclass.cbWndExtra = 0;
 wndclass.hInstance = hInstance;
 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 wndclass.lpszMenuName = NULL;
 wndclass.lpszClassName = "CWindow";
 RegisterClass(&wndclass);
 hwMain = CreateWindow("CWindow", "Phong Shading(is Testing)",
          WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
          WIDTH*2+22,HEIGHT*2+36+6, NULL, NULL, hInstance, NULL);
    SetClientSize(hwMain,320*2,240*2);//ウィンドウサイズ丁度良いサイズに調整してくれる
 /* ウインドウを表示 */
 ShowWindow(hwMain, iCmdShow);
 UpdateWindow(hwMain);
 /* メッセージループ */
 while(GetMessage(&msg, NULL, 0, 0)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
 return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
    HDC hdc;
    PAINTSTRUCT ps;
 static int DrawMode = 0;
 static int prevSpace = 0;
    switch (iMsg) {
    case WM_TIMER:
        {
            if(GetAsyncKeyState(VK_ESCAPE)<0)
                SendMessage(hwnd,WM_DESTROY,NULL,NULL);
            InvalidateRect(hwnd,NULL,NULL);
        }
        return 0;
    case WM_CREATE:
        SetTimer(hwnd,100,1000/900,NULL);
        /* BITMAPINFOをゼロクリア */
        ZeroMemory(&biInfo, sizeof(BITMAPINFO));
        /* BITMAPINFO設定 */
        biInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        biInfo.bmiHeader.biWidth = WIDTH;
        biInfo.bmiHeader.biHeight = -HEIGHT;
        biInfo.bmiHeader.biPlanes = 1;
        biInfo.bmiHeader.biBitCount = 32;
        biInfo.bmiHeader.biCompression = BI_RGB;
        /* ウインドウのDCを取得 */
        hdc = GetDC(hwnd);
        /* biInfoの形式でDIBSectionを作成 */
        hBMP =CreateDIBSection(hdc, &biInfo, DIB_RGB_COLORS, (LPVOID*)(&lpPixel), NULL, 0);
        /* DIBSection用のメモリDCを作成 */
        hdcBMP = CreateCompatibleDC(hdc);
        /* メモリDCにDIBSectionを選択 */
        hBMPOLD = (HBITMAP)SelectObject(hdcBMP, hBMP);
        /* 不要になったウインドウのDCを解放 */
        ReleaseDC(hwnd, hdc);
        return 0;
    case WM_PAINT:
        {
  int i;
        hdc = BeginPaint(hwnd, &ps);
        ClearScreen();//画面を消す
        PSET(160,120,0x00ff0000);//点を打つ
        LINE(0,30,120,150,0x00ff0000);//線を描く
        /* DIBSectionをDIBとして描画 */
            StretchDIBits(hdc, 0, 0, WIDTH*2, HEIGHT*2,
                0, 0, WIDTH, HEIGHT, lpPixel, &biInfo,
                DIB_RGB_COLORS,SRCCOPY);
            CalcFPS(hdc);
            EndPaint(hwnd, &ps);
            DeleteObject(SelectObject(hdc , GetStockObject(WHITE_BRUSH)));
        }
        return 0;
    case WM_DESTROY: /* ウインドウ破棄時 */
        SelectObject(hdcBMP, hBMPOLD);
        DeleteObject(hdcBMP);
        DeleteObject(hBMP);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, iMsg, wParam, lParam);
}

003:透視投影
やっと3D座標から2D画面に変換できるようになります。
他の方はどうかは知りませんが、自分はこの透視投影と
2Dポリゴンで3次元空間を自前の3Dエンジン?で表現していました。
透視投影.png
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define WIDTH 320
#define HEIGHT 240
int min_[HEIGHT];
int max_[HEIGHT];
#define SCREENHEIGHT HEIGHT
#define SCALE 120.0f
int MAXVTX=0;//最大の頂点数
int MAXPOL=0;//最大のポリゴン数
int LastLine=0;
BITMAPINFO biInfo;
LPDWORD lpPixel;
struct TClipSize
{
 int width,height;
};
struct TClipBltInfo
{
 int sw,sh;
 int sx,sy;
 int dx,dy;
};
HBITMAP hBMP, hBMPOLD;
HDC hdcBMP;
HDC HdcGlobal;
char FileStr[10000][800];
#define MAX_XYZ 5000
typedef struct XYZ {
 double x;
 double y;
 double z;
 int count;
}XYZ;
typedef struct XY {
 double x;
 double y;
}XY;
typedef struct COL {
 int r;
 int g;
 int b;
}COL;
XYZ Pnt[MAX_XYZ];
XY Pnt2[MAX_XYZ];
inline void PSET(int x, int y, int col);//点を打つ関数
int FPSCount=0;
int D=424;double vpz=-800.0f;
double vpx=1.0f;double vpy=1.0f;
int Poly[5000][4];
void CalcFPS(HDC hdc)
{
 char str[100];
 static int FPS=0;
 static long int time=GetTickCount();
 //FPSCount++;
 if(GetTickCount()-time>1000)
 {
  time=GetTickCount();
  FPS=FPSCount;
  FPSCount=0;
 }else{
  FPSCount++;
 }
 sprintf(str,"FPS=%d",FPS);
 TextOut(hdc,0,0,str,strlen(str));
 //TextOut(hdc,0,90,"PRESS SPACE KEY!!",strlen("PRESS SPACE KEY!!"));
}
BOOL SetClientSize(HWND hWnd, int width, int height)
{
 RECT rw, rc;
 ::GetWindowRect(hWnd, &rw);
 ::GetClientRect(hWnd, &rc);
 int new_width = (rw.right - rw.left) - (rc.right - rc.left) + width;
 int new_height = (rw.bottom - rw.top) - (rc.bottom - rc.top) + height;
 return ::SetWindowPos(hWnd, NULL, 0, 0, new_width, new_height, SWP_NOMOVE | SWP_NOZORDER);
}
inline void ClearScreen()
{
 for(int i=0;i<480;i++)
 {
  for(int j=0;j<640;j++)
  {
   PSET(j,i,0x00000000);
  }
 }
}
inline void PSET(int x, int y, int col)
{
 int R = (col >> 16) & 0xFF;
 int G = (col >> 8) & 0xFF;
 int B = col & 0xFF;
 int address;
 //点を打つ
 if (x >= 0 && y >= 0 && x < WIDTH && y < HEIGHT)
 {
  address = x + (y * WIDTH);
  lpPixel[address] = RGB(B,G,R);
 }
}
inline void LINE(int x1,int y1,int x2,int y2,int col)
{
    //線分を描画する
    for(int i=0;i<=1024;i++)
    {
        int ax=(x1*(1024-i)+x2*i)>>10;
        int ay=(y1*(1024-i)+y2*i)>>10;
        PSET(ax,ay,col);
    }
}
void Projection2DAxisFrom3DAxis(HWND hwnd)
{
 HDC hdc = GetDC(hwnd);
    for(int i=0;i<8;i++)
    {
        Pnt2[i].x=(Pnt[i].x*D)/(Pnt[i].z-vpz)+(WIDTH/2);
        Pnt2[i].y=-(Pnt[i].y*D)/(Pnt[i].z-vpz)+(HEIGHT/2);
        PSET(Pnt2[i].x,Pnt2[i].y,0x00ffffff);
  //char str[100];
  //sprintf(str,"%d",i);
  //TextOut(hdc,Pnt2[i].x,Pnt2[i].y,str,strlen(str));
  //TextOut(hdc, 0, i*20, str, strlen(str));
    }
}
void SetPoly(int num,int n1,int n2,int n3,int n4)
{
 Poly[num][0]=n1;//頂点番号を指定
 Poly[num][1]=n2;
 Poly[num][2]=n3;
 Poly[num][3]=n4;
}
void SetPoly(int num,int n1,int n2,int n3)
{
 Poly[num][0]=n1;//頂点番号を指定
 Poly[num][1]=n2;
 Poly[num][2]=n3;
}
void SetPoly(int n1,int n2,int n3)
{
 static int num=0;
 Poly[num][0]=n1;//頂点番号を指定
 Poly[num][1]=n2;
 Poly[num][2]=n3;
 num++;
}
void SetXYZ(int num,double x,double y,double z)
{
    if(num<0 || num>=MAX_XYZ) return;
    Pnt[num].x=x;//頂点座標を指定
    Pnt[num].y=y;
    Pnt[num].z=z;
}
void Init()
{
    SetXYZ(0,-100.0,-100.0,-100.0);//8つの頂点を指定している
    SetXYZ(1, 100.0,-100.0,-100.0);
    SetXYZ(2, 100.0, 100.0,-100.0);
    SetXYZ(3,-100.0, 100.0,-100.0);
    SetXYZ(4,-100.0,-100.0, 100.0);
    SetXYZ(5, 100.0,-100.0, 100.0);
    SetXYZ(6, 100.0, 100.0, 100.0);
    SetXYZ(7,-100.0, 100.0, 100.0);
 SetPoly(0,1,2);
 SetPoly(2,3,0);
 SetPoly(3,4,0);
 SetPoly(3,7,4);
 SetPoly(7,6,5);
 SetPoly(7,5,4);
 SetPoly(6,2,1);
 SetPoly(6,1,5);
 SetPoly(5,1,0);
 SetPoly(0,4,5);
 SetPoly(6,7,3);
 SetPoly(6,3,2);
 MAXVTX = 7;
}
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE hInst;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow){
 HWND hwMain;
 MSG msg;
 WNDCLASS wndclass;
 hInst = hInstance;
 wndclass.style = CS_HREDRAW | CS_VREDRAW;
 wndclass.lpfnWndProc = WndProc;
 wndclass.cbClsExtra = 0;
 wndclass.cbWndExtra = 0;
 wndclass.hInstance = hInstance;
 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 wndclass.lpszMenuName = NULL;
 wndclass.lpszClassName = "CWindow";
 RegisterClass(&wndclass);
 hwMain = CreateWindow("CWindow", "Phong Shading(is Testing)",
          WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
          WIDTH*2+22,HEIGHT*2+36+6, NULL, NULL, hInstance, NULL);
    SetClientSize(hwMain,320*2,240*2);//ウィンドウサイズ丁度良いサイズに調整してくれる
 /* ウインドウを表示 */
 ShowWindow(hwMain, iCmdShow);
 UpdateWindow(hwMain);
 /* メッセージループ */
 while(GetMessage(&msg, NULL, 0, 0)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
 return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
    HDC hdc;
    PAINTSTRUCT ps;
 static int DrawMode = 0;
 static int prevSpace = 0;
    switch (iMsg) {
    case WM_TIMER:
        {
            if(GetAsyncKeyState(VK_ESCAPE)<0)
                SendMessage(hwnd,WM_DESTROY,NULL,NULL);
            InvalidateRect(hwnd,NULL,NULL);
        }
        return 0;
    case WM_CREATE:
  Init();
        SetTimer(hwnd,100,1000/900,NULL);
        /* BITMAPINFOをゼロクリア */
        ZeroMemory(&biInfo, sizeof(BITMAPINFO));
        /* BITMAPINFO設定 */
        biInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        biInfo.bmiHeader.biWidth = WIDTH;
        biInfo.bmiHeader.biHeight = -HEIGHT;
        biInfo.bmiHeader.biPlanes = 1;
        biInfo.bmiHeader.biBitCount = 32;
        biInfo.bmiHeader.biCompression = BI_RGB;
        /* ウインドウのDCを取得 */
        hdc = GetDC(hwnd);
        /* biInfoの形式でDIBSectionを作成 */
        hBMP =CreateDIBSection(hdc, &biInfo, DIB_RGB_COLORS, (LPVOID*)(&lpPixel), NULL, 0);
        /* DIBSection用のメモリDCを作成 */
        hdcBMP = CreateCompatibleDC(hdc);
        /* メモリDCにDIBSectionを選択 */
        hBMPOLD = (HBITMAP)SelectObject(hdcBMP, hBMP);
        /* 不要になったウインドウのDCを解放 */
        ReleaseDC(hwnd, hdc);
        return 0;
    case WM_PAINT:
        {
  int i;
        hdc = BeginPaint(hwnd, &ps);
        ClearScreen();//画面を消す
        PSET(160,120,0x00ff0000);//点を打つ
        LINE(0,30,300,150,0x00ff0000);//線を描く
  Projection2DAxisFrom3DAxis(hwnd);
        /* DIBSectionをDIBとして描画 */
            StretchDIBits(hdc, 0, 0, WIDTH*2, HEIGHT*2,
                0, 0, WIDTH, HEIGHT, lpPixel, &biInfo,
                DIB_RGB_COLORS,SRCCOPY);
            CalcFPS(hdc);
            EndPaint(hwnd, &ps);
            DeleteObject(SelectObject(hdc , GetStockObject(WHITE_BRUSH)));
        }
        return 0;
    case WM_DESTROY: /* ウインドウ破棄時 */
        SelectObject(hdcBMP, hBMPOLD);
        DeleteObject(hdcBMP);
        DeleteObject(hBMP);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
004:ワイヤーフレーム
ワイヤーフレーム.png
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define WIDTH 320
#define HEIGHT 240
int min_[HEIGHT];
int max_[HEIGHT];
#define SCREENHEIGHT HEIGHT
#define SCALE 120.0f
int MAXVTX=0;//最大の頂点数
int MAXPOL=0;//最大のポリゴン数
int LastLine=0;
BITMAPINFO biInfo;
LPDWORD lpPixel;
struct TClipSize
{
 int width,height;
};
struct TClipBltInfo
{
 int sw,sh;
 int sx,sy;
 int dx,dy;
};
HBITMAP hBMP, hBMPOLD;
HDC hdcBMP;
HDC HdcGlobal;
char FileStr[10000][800];
#define MAX_XYZ 5000
typedef struct XYZ {
 double x;
 double y;
 double z;
 int count;
}XYZ;
typedef struct XY {
 double x;
 double y;
}XY;
typedef struct COL {
 int r;
 int g;
 int b;
}COL;
XYZ Pnt[MAX_XYZ];
XY Pnt2[MAX_XYZ];
int Wire[5000][2];
inline void PSET(int x, int y, int col);//点を打つ関数
int FPSCount=0;
int D=424;double vpz=-800.0f;
double vpx=1.0f;double vpy=1.0f;
int Poly[5000][4];
void CalcFPS(HDC hdc)
{
 char str[100];
 static int FPS=0;
 static long int time=GetTickCount();
 //FPSCount++;
 if(GetTickCount()-time>1000)
 {
  time=GetTickCount();
  FPS=FPSCount;
  FPSCount=0;
 }else{
  FPSCount++;
 }
 sprintf(str,"FPS=%d",FPS);
 TextOut(hdc,0,0,str,strlen(str));
 //TextOut(hdc,0,90,"PRESS SPACE KEY!!",strlen("PRESS SPACE KEY!!"));
}
BOOL SetClientSize(HWND hWnd, int width, int height)
{
 RECT rw, rc;
 ::GetWindowRect(hWnd, &rw);
 ::GetClientRect(hWnd, &rc);
 int new_width = (rw.right - rw.left) - (rc.right - rc.left) + width;
 int new_height = (rw.bottom - rw.top) - (rc.bottom - rc.top) + height;
 return ::SetWindowPos(hWnd, NULL, 0, 0, new_width, new_height, SWP_NOMOVE | SWP_NOZORDER);
}
inline void ClearScreen()
{
 for(int i=0;i<480;i++)
 {
  for(int j=0;j<640;j++)
  {
   PSET(j,i,0x00000000);
  }
 }
}
inline void PSET(int x, int y, int col)
{
 int R = (col >> 16) & 0xFF;
 int G = (col >> 8) & 0xFF;
 int B = col & 0xFF;
 int address;
 //点を打つ
 if (x >= 0 && y >= 0 && x < WIDTH && y < HEIGHT)
 {
  address = x + (y * WIDTH);
  lpPixel[address] = RGB(B,G,R);
 }
}
inline void LINE(int x1,int y1,int x2,int y2,int col)
{
    //線分を描画する
    for(int i=0;i<=1024;i++)
    {
        int ax=(x1*(1024-i)+x2*i)>>10;
        int ay=(y1*(1024-i)+y2*i)>>10;
        PSET(ax,ay,col);
    }
}
void Projection2DAxisFrom3DAxis(HWND hwnd)
{
 HDC hdc = GetDC(hwnd);
    for(int i=0;i<8;i++)
    {
        Pnt2[i].x=(Pnt[i].x*D)/(Pnt[i].z-vpz)+(WIDTH/2);
        Pnt2[i].y=-(Pnt[i].y*D)/(Pnt[i].z-vpz)+(HEIGHT/2);
        PSET(Pnt2[i].x,Pnt2[i].y,0x00ffffff);
  //char str[100];
  //sprintf(str,"%d",i);
  //TextOut(hdc,Pnt2[i].x,Pnt2[i].y,str,strlen(str));
  //TextOut(hdc, 0, i*20, str, strlen(str));
    }
}
void SetWire(int num,int n1,int n2)
{
 Wire[num][0]=n1;
 Wire[num][1]=n2;
}
void SetPoly(int num,int n1,int n2,int n3,int n4)
{
 Poly[num][0]=n1;//頂点番号を指定
 Poly[num][1]=n2;
 Poly[num][2]=n3;
 Poly[num][3]=n4;
}
void SetPoly(int num,int n1,int n2,int n3)
{
 Poly[num][0]=n1;//頂点番号を指定
 Poly[num][1]=n2;
 Poly[num][2]=n3;
}
void SetPoly(int n1,int n2,int n3)
{
 static int num=0;
 Poly[num][0]=n1;//頂点番号を指定
 Poly[num][1]=n2;
 Poly[num][2]=n3;
 num++;
}
void SetXYZ(int num,double x,double y,double z)
{
    if(num<0 || num>=MAX_XYZ) return;
    Pnt[num].x=x;//頂点座標を指定
    Pnt[num].y=y;
    Pnt[num].z=z;
}
void Init()
{
    SetXYZ(0,-100.0,-100.0,-100.0);//8つの頂点を指定している
    SetXYZ(1, 100.0,-100.0,-100.0);
    SetXYZ(2, 100.0, 100.0,-100.0);
    SetXYZ(3,-100.0, 100.0,-100.0);
    SetXYZ(4,-100.0,-100.0, 100.0);
    SetXYZ(5, 100.0,-100.0, 100.0);
    SetXYZ(6, 100.0, 100.0, 100.0);
    SetXYZ(7,-100.0, 100.0, 100.0);
 SetWire(0,0,1);
 SetWire(1,1,2);
 SetWire(2,2,3);
 SetWire(3,3,0);
 SetWire(4,4,5);
 SetWire(5,5,6);
 SetWire(6,6,7);
 SetWire(7,7,4);
 SetWire(8,0,4);
 SetWire(9,1,5);
 SetWire(10,2,6);
 SetWire(11,3,7);
 SetPoly(0,1,2);
 SetPoly(2,3,0);
 SetPoly(3,4,0);
 SetPoly(3,7,4);
 SetPoly(7,6,5);
 SetPoly(7,5,4);
 SetPoly(6,2,1);
 SetPoly(6,1,5);
 SetPoly(5,1,0);
 SetPoly(0,4,5);
 SetPoly(6,7,3);
 SetPoly(6,3,2);
 MAXVTX = 7;
}
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE hInst;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow){
 HWND hwMain;
 MSG msg;
 WNDCLASS wndclass;
 hInst = hInstance;
 wndclass.style = CS_HREDRAW | CS_VREDRAW;
 wndclass.lpfnWndProc = WndProc;
 wndclass.cbClsExtra = 0;
 wndclass.cbWndExtra = 0;
 wndclass.hInstance = hInstance;
 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 wndclass.lpszMenuName = NULL;
 wndclass.lpszClassName = "CWindow";
 RegisterClass(&wndclass);
 hwMain = CreateWindow("CWindow", "Phong Shading(is Testing)",
          WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
          WIDTH*2+22,HEIGHT*2+36+6, NULL, NULL, hInstance, NULL);
    SetClientSize(hwMain,320*2,240*2);//ウィンドウサイズ丁度良いサイズに調整してくれる
 /* ウインドウを表示 */
 ShowWindow(hwMain, iCmdShow);
 UpdateWindow(hwMain);
 /* メッセージループ */
 while(GetMessage(&msg, NULL, 0, 0)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
 return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {
    HDC hdc;
    PAINTSTRUCT ps;
 static int DrawMode = 0;
 static int prevSpace = 0;
    switch (iMsg) {
    case WM_TIMER:
        {
            if(GetAsyncKeyState(VK_ESCAPE)<0)
                SendMessage(hwnd,WM_DESTROY,NULL,NULL);
            InvalidateRect(hwnd,NULL,NULL);
        }
        return 0;
    case WM_CREATE:
  Init();
        SetTimer(hwnd,100,1000/900,NULL);
        /* BITMAPINFOをゼロクリア */
        ZeroMemory(&biInfo, sizeof(BITMAPINFO));
        /* BITMAPINFO設定 */
        biInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        biInfo.bmiHeader.biWidth = WIDTH;
        biInfo.bmiHeader.biHeight = -HEIGHT;
        biInfo.bmiHeader.biPlanes = 1;
        biInfo.bmiHeader.biBitCount = 32;
        biInfo.bmiHeader.biCompression = BI_RGB;
        /* ウインドウのDCを取得 */
        hdc = GetDC(hwnd);
        /* biInfoの形式でDIBSectionを作成 */
        hBMP =CreateDIBSection(hdc, &biInfo, DIB_RGB_COLORS, (LPVOID*)(&lpPixel), NULL, 0);
        /* DIBSection用のメモリDCを作成 */
        hdcBMP = CreateCompatibleDC(hdc);
        /* メモリDCにDIBSectionを選択 */
        hBMPOLD = (HBITMAP)SelectObject(hdcBMP, hBMP);
        /* 不要になったウインドウのDCを解放 */
        ReleaseDC(hwnd, hdc);
        return 0;
    case WM_PAINT:
        {
  int i;
        hdc = BeginPaint(hwnd, &ps);
        ClearScreen();//画面を消す
        //PSET(160,120,0x00ff0000);//点を打つ
        //LINE(0,30,300,150,0x00ff0000);//線を描く
  Projection2DAxisFrom3DAxis(hwnd);
  for(int i=0;i<12;i++)
  {
   LINE(Pnt2[Wire[i][0]].x,Pnt2[Wire[i][0]].y,Pnt2[Wire[i][1]].x,Pnt2[Wire[i][1]].y,
    0xff00ff);
  }
        /* DIBSectionをDIBとして描画 */
            StretchDIBits(hdc, 0, 0, WIDTH*2, HEIGHT*2,
                0, 0, WIDTH, HEIGHT, lpPixel, &biInfo,
                DIB_RGB_COLORS,SRCCOPY);
            CalcFPS(hdc);
            EndPaint(hwnd, &ps);
            DeleteObject(SelectObject(hdc , GetStockObject(WHITE_BRUSH)));
        }
        return 0;
    case WM_DESTROY: /* ウインドウ破棄時 */
        SelectObject(hdcBMP, hBMPOLD);
        DeleteObject(hdcBMP);
        DeleteObject(hBMP);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
005:フラットシェーディング(コンスタントシェーディング)

さてようやくポリゴンを描画するのですが、
初代バーチャファイターっぽいポリゴンの種類でカクカクなのですが、
一応フラットシェーディングと呼びます。

この場合、3角形の単位で描画したいのですが、

直線のアルゴリズムを3つの辺それぞれに適応しますが、
その際に、どちらがポリゴンの左側でどちらがポリゴンの右側なのかを
minX[]とmaxX[]
(この際、LeftX[]かRightX[]という名前の配列でも良いのですが)
という配列に記録します。

すると、あとはfor(y=0;y<HEIGHT;y++)
{
}
のループ内で描画すればOKです。
ここで注意したいのは、minXとmaxXは画面の高さ分だけ宣言するのと、
ポリゴンを描画する際に必ずminXとmaxXは最大と最小に
代入しておく必要があります。で、ループ内でそのままだったら、
continue;でループをスキップしつつ続ける必要があります。で、これで、三角形自体は描画できるようになりましたが、何かが足りません。そうです、明るさです。三角形の右ねじの方向の法線ベクトルを求めて、
光源ベクトルとの内積で明るさを求めます。
sankaku.png

法線ベクトルは大学数学の外積で求まります。
Ax=X1-X2:Ay=Y1-Y2:Az=Z1-Z2
Bx=X1-X3:By=Y1-Y3:Bz=Z1-Z3

GaiX=Ay*Bz-Az*By
GaiY=Az*Bx-Ax*Bz
GaiZ=Ax*By-Ay*Bx

内積は、光源ベクトルがLx,Ly,Lzなら
R=Lx*GaiX+Ly*GaiY+Lz*GaiZ
で求まります。
立方体.png

一応手動でモデリングをしたのでいろいろと間違っていますが
次の章でwavefrontのobjファイルを読み込むので大丈夫です。
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define RAD (3.14159265/180.0)

#define WIDTH 320
#define HEIGHT 240

int min_[HEIGHT];
int max_[HEIGHT];
int minX[HEIGHT];
int maxX[HEIGHT];

#define SCREENHEIGHT HEIGHT

#define SCALE 120.0f

int MAXVTX=0;//最大の頂点数
int MAXPOL=0;//最大のポリゴン数
int LastLine=0;

BITMAPINFO biInfo;
LPDWORD lpPixel;
struct TClipSize
{
 int width,height;
};

struct TClipBltInfo
{
 int sw,sh;
 int sx,sy;
 int dx,dy;
};
HBITMAP hBMP, hBMPOLD;
HDC hdcBMP;
HDC HdcGlobal;
char FileStr[10000][800];

#define MAX_XYZ 5000
typedef struct XYZ {
 double x;
 double y;
 double z;
 int count;
}XYZ;
typedef struct XY {
 double x;
 double y;
}XY;
typedef struct COL {
 int r;
 int g;
 int b;
}COL;
XYZ Pnt[MAX_XYZ];
XY Pnt2[MAX_XYZ];

int Wire[5000][2];

inline void PSET(int x, int y, int col);//点を打つ関数
int FPSCount=0;
int D=424;double vpz=-800.0f;
double vpx=1.0f;double vpy=1.0f;
int Poly[5000][4];

void Rotate(int X,int Y,int Z)
{
 int i;
 double cosSita=cos(Z*RAD);
 double sinSita=sin(Z*RAD);
    for(i=0;i<MAXVTX;i++)//Z軸、つまり2次元的な回転を行う(矢印キー右左で左右に回転する)
    {
        double x2=Pnt[i].x*cosSita-Pnt[i].y*sinSita;
        double y2=Pnt[i].x*sinSita+Pnt[i].y*cosSita;
        double z2=Pnt[i].z;

  //double x2_=vvv[i].x*cosSita-vvv[i].y*sinSita;
        //double y2_=vvv[i].x*sinSita+vvv[i].y*cosSita;
        //double z2_=vvv[i].z;


        Pnt[i].x=x2;
        Pnt[i].y=y2;
        Pnt[i].z=z2;

  //vvv[i].x=x2_;
        //vvv[i].y=y2_;
        //vvv[i].z=z2_;

    }

 double cosSita2=cos(X*RAD);
 double sinSita2=sin(X*RAD);
    for(i=0;i<MAXVTX;i++)//X軸、つまり奥か手前かに回転する(矢印キー↑↓で↑↓に回転する)
    {
        double y2=Pnt[i].y*cosSita2-Pnt[i].z*sinSita2;
        double z2=Pnt[i].y*sinSita2+Pnt[i].z*cosSita2;
        double x2=Pnt[i].x;

  //double y2_=vvv[i].y*cosSita2-vvv[i].z*sinSita2;
        //double z2_=vvv[i].y*sinSita2+vvv[i].z*cosSita2;
        //double x2_=vvv[i].x;

        Pnt[i].x=x2;
        Pnt[i].y=y2;
        Pnt[i].z=z2;

  //vvv[i].x=x2_;
        //vvv[i].y=y2_;
        //vvv[i].z=z2_;

    }

    for(i=0;i<MAXVTX;i++)//ここはY軸回転だが、今のところ使わない
    {
        double x3=Pnt[i].z*sin(Y*RAD)+Pnt[i].x*cos(Y*RAD);
        double z3=Pnt[i].z*cos(Y*RAD)-Pnt[i].x*sin(Y*RAD);
        double y3=Pnt[i].y;

  //double x3_=vvv[i].z*sin(Y*RAD)+vvv[i].x*cos(Y*RAD);
        //double z3_=vvv[i].z*cos(Y*RAD)-vvv[i].x*sin(Y*RAD);
        //double y3_=vvv[i].y;


        Pnt[i].x=x3;
        Pnt[i].y=y3;
        Pnt[i].z=z3;

  //vvv[i].x=x3_;
        //vvv[i].y=y3_;
        //vvv[i].z=z3_;

    }
}


void CalcFPS(HDC hdc)
{

 char str[100];
 static int FPS=0;
 static long int time=GetTickCount();
 //FPSCount++;

 if(GetTickCount()-time>1000)
 {
  time=GetTickCount();
  FPS=FPSCount;
  FPSCount=0;

 }else{
  FPSCount++;
 }
 sprintf(str,"FPS=%d",FPS);
 TextOut(hdc,0,0,str,strlen(str));

 //TextOut(hdc,0,90,"PRESS SPACE KEY!!",strlen("PRESS SPACE KEY!!"));
}


BOOL SetClientSize(HWND hWnd, int width, int height)
{
 RECT rw, rc;
 ::GetWindowRect(hWnd, &rw);
 ::GetClientRect(hWnd, &rc);

 int new_width = (rw.right - rw.left) - (rc.right - rc.left) + width;
 int new_height = (rw.bottom - rw.top) - (rc.bottom - rc.top) + height;

 return ::SetWindowPos(hWnd, NULL, 0, 0, new_width, new_height, SWP_NOMOVE | SWP_NOZORDER);
}

inline void ClearScreen()
{
 for(int i=0;i<480;i++)
 {
  for(int j=0;j<640;j++)
  {
   PSET(j,i,0x00000000);
  }
 }
}

inline void PSET(int x, int y, int col)
{
 int R = (col >> 16) & 0xFF;
 int G = (col >> 8) & 0xFF;
 int B = col & 0xFF;
 int address;
 //点を打つ
 if (x >= 0 && y >= 0 && x < WIDTH && y < HEIGHT)
 {
  address = x + (y * WIDTH);
  lpPixel[address] = RGB(B,G,R);
 }
}

inline void LINE(int x1,int y1,int x2,int y2,int col)
{
    //線分を描画する
    for(int i=0;i<=1024;i++)
    {
        int ax=(x1*(1024-i)+x2*i)>>10;
        int ay=(y1*(1024-i)+y2*i)>>10;
        PSET(ax,ay,col);
    }
}

void Projection2DAxisFrom3DAxis(HDC hdc)
{
 //HDC hdc = GetDC(hwnd);

    for(int i=0;i<8;i++)
    {
        Pnt2[i].x=(Pnt[i].x*D)/(Pnt[i].z-vpz)+(WIDTH/2);
        Pnt2[i].y=-(Pnt[i].y*D)/(Pnt[i].z-vpz)+(HEIGHT/2);
        //PSET(Pnt2[i].x,Pnt2[i].y,0x00ffffff);
  ///char str[100];
  //sprintf(str,"%d",i);
  //TextOut(hdc,Pnt2[i].x+WIDTH/2,Pnt2[i].y+HEIGHT/2,str,strlen(str));
  //TextOut(hdc, 0, i*20, str, strlen(str));
    }
}

void SetWire(int num,int n1,int n2)
{
 Wire[num][0]=n1;
 Wire[num][1]=n2;
}

void SetPoly(int num,int n1,int n2,int n3,int n4)
{
 Poly[num][0]=n1;//頂点番号を指定
 Poly[num][1]=n2;
 Poly[num][2]=n3;
 Poly[num][3]=n4;
}

void SetPoly(int num,int n1,int n2,int n3)
{
 Poly[num][0]=n1;//頂点番号を指定
 Poly[num][1]=n2;
 Poly[num][2]=n3;
}

void SetPoly(int n1,int n2,int n3)
{
 static int num=0;
 Poly[num][0]=n1;//頂点番号を指定
 Poly[num][1]=n2;
 Poly[num][2]=n3;
 num++;

}

void SetXYZ(int num,double x,double y,double z)
{
    if(num<0 || num>=MAX_XYZ) return;
    Pnt[num].x=x;//頂点座標を指定
    Pnt[num].y=y;
    Pnt[num].z=z;
}

void Init()
{
    SetXYZ(0,-100.0,-100.0,-100.0);//8つの頂点を指定している
    SetXYZ(1, 100.0,-100.0,-100.0);
    SetXYZ(2, 100.0, 100.0,-100.0);
    SetXYZ(3,-100.0, 100.0,-100.0);
    SetXYZ(4,-100.0,-100.0, 100.0);
    SetXYZ(5, 100.0,-100.0, 100.0);
    SetXYZ(6, 100.0, 100.0, 100.0);
    SetXYZ(7,-100.0, 100.0, 100.0);

 SetWire(0,0,1);
 SetWire(1,1,2);
 SetWire(2,2,3);
 SetWire(3,3,0);
 SetWire(4,4,5);
 SetWire(5,5,6);
 SetWire(6,6,7);
 SetWire(7,7,4);

 SetWire(8,0,4);
 SetWire(9,1,5);
 SetWire(10,2,6);
 SetWire(11,3,7);

 SetPoly(0,1,0,3);//三辺のポリゴンを頂点番号で指定している
 SetPoly(1,1,2,3);
 SetPoly(2,1,5,2);
 SetPoly(3,5,6,2);
 SetPoly(4,3,2,7);
 SetPoly(5,6,7,2);
 SetPoly(6,4,0,7);
 SetPoly(7,0,3,7);
 SetPoly(8,4,1,0);
 SetPoly(9,1,4,5);
 SetPoly(10,5,4,7);
 SetPoly(11,5,7,6);
 SetPoly(12,0,1,3);

 MAXVTX = 8;

}

inline void ScanEdge(int x1,int y1,int x2,int y2)
{
 //2次元的なスキャン

    for(int i=0;i<=1024;i++)
    {
        int ax=x1*((1<<10)-i)+x2*i>>10;
        int ay=y1*((1<<10)-i)+y2*i>>10;

        if(ay<0 || ay>=HEIGHT) continue;
  if(minX[ay]>ax) minX[ay]=ax;
  if(maxX[ay]<ax) maxX[ay]=ax;

    }

inline void TRIANGLE(int x1,int y1,int x2,int y2,int x3,int y3,int col)
{

 for(int i=0;i<HEIGHT;i++)
 {
  minX[i]=65536;
  maxX[i]=-65536;

 }

    ScanEdge(x1,y1,x2,y2);
    ScanEdge(x1,y1,x3,y3);
    ScanEdge(x2,y2,x3,y3);

    for(int y=0;y<HEIGHT;y++)
    {
        if(minX[y]==65536 || maxX[y]==-65536) continue;

        for(int x=minX[y];x<=maxX[y];x++)
  {

            PSET(x,y,col);
  }
    }
}

void Normalize(XYZ *a)//正規化関Calc
{
   double len=sqrt(a->x*a->x+a->y*a->y+a->z*a->z);
   a->x/=len;
   a->y/=len;
   a->z/=len;
}


XYZ CalcCross(XYZ a,XYZ b,XYZ c)
{
 XYZ d={0.0f};
 XYZ a2,b2;

   a2.x=a.x-b.x;
   a2.y=a.y-b.y;
   a2.z=a.z-b.z;

   b2.x=a.x-c.x;
   b2.y=a.y-c.y;
   b2.z=a.z-c.z;

   Normalize(&a2);
   //double len=sqrt(a2.x*a2.x+a2.y*a2.y+a2.z*a2.z+0.1);
   //a2.x/=len;
   //a2.y/=len;
   //a2.z/=len;

   Normalize(&b2);
   //double len2=sqrt(b2.x*b2.x+b2.y*b2.y+b2.z*b2.z+0.1);
   //b2.x/=len2;
   //b2.y/=len2;
   //b2.z/=len2;


            //23-32 31-13 12-21
            //aY*bZ-aZ*bY aZ*bX-aX*bZ aX*bY-aY*bX
   d.x=a2.y*b2.z-a2.z*b2.y;
   d.y=a2.z*b2.x-a2.x*b2.z;
   d.z=a2.x*b2.y-a2.y*b2.x;

  return d;
}

double CalcDotProduct(XYZ a, XYZ b)
{
 return a.x * b.x + a.y * b.y + a.z * b.z;
}


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE hInst;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PSTR szCmdLine, int iCmdShow){

 HWND hwMain;

 MSG msg;
 WNDCLASS wndclass;

 hInst = hInstance;

 wndclass.style = CS_HREDRAW | CS_VREDRAW;
 wndclass.lpfnWndProc = WndProc;
 wndclass.cbClsExtra = 0;
 wndclass.cbWndExtra = 0;
 wndclass.hInstance = hInstance;
 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 wndclass.lpszMenuName = NULL;
 wndclass.lpszClassName = "CWindow";

 RegisterClass(&wndclass);

 hwMain = CreateWindow("CWindow", "Phong Shading(is Testing)",
          WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
          WIDTH*2+22,HEIGHT*2+36+6, NULL, NULL, hInstance, NULL);

    SetClientSize(hwMain,320*2,240*2);//ウィンドウサイズ丁度良いサイズに調整してくれる

 /* ウインドウを表示 */
 ShowWindow(hwMain, iCmdShow);
 UpdateWindow(hwMain);

 /* メッセージループ */
 while(GetMessage(&msg, NULL, 0, 0)) {

  TranslateMessage(&msg);
  DispatchMessage(&msg);

 }

 return msg.wParam;

}




LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) {



    HDC hdc;
    PAINTSTRUCT ps;
 static int DrawMode = 0;
 static int prevSpace = 0;

    switch (iMsg) {

    case WM_TIMER:
        {
            if(GetAsyncKeyState(VK_ESCAPE)<0)
                SendMessage(hwnd,WM_DESTROY,NULL,NULL);
            InvalidateRect(hwnd,NULL,NULL);

   double X=0.0f;
            double Y=0.0f;
            double Z=0.0f;

            //Z+=0.1f;
            //Y+=0.1f;
            if(GetAsyncKeyState(VK_RIGHT)<0) Y=-2.5f;
            else if(GetAsyncKeyState(VK_LEFT)<0) Y=2.5f;
            else Y=0.0f;

            if(GetAsyncKeyState(VK_DOWN)<0) X=-2.5f;
            else if(GetAsyncKeyState(VK_UP)<0) X=2.5f;
            else X=0.0f;

   Rotate(X,Y,0);
        }
        return 0;
    case WM_CREATE:
  Init();

        SetTimer(hwnd,100,1000/900,NULL);

        /* BITMAPINFOをゼロクリア */
        ZeroMemory(&biInfo, sizeof(BITMAPINFO));

        /* BITMAPINFO設定 */
        biInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        biInfo.bmiHeader.biWidth = WIDTH;
        biInfo.bmiHeader.biHeight = -HEIGHT;
        biInfo.bmiHeader.biPlanes = 1;
        biInfo.bmiHeader.biBitCount = 32;
        biInfo.bmiHeader.biCompression = BI_RGB;

        /* ウインドウのDCを取得 */
        hdc = GetDC(hwnd);

        /* biInfoの形式でDIBSectionを作成 */
        hBMP =CreateDIBSection(hdc, &biInfo, DIB_RGB_COLORS, (LPVOID*)(&lpPixel), NULL, 0);

        /* DIBSection用のメモリDCを作成 */
        hdcBMP = CreateCompatibleDC(hdc);

        /* メモリDCにDIBSectionを選択 */
        hBMPOLD = (HBITMAP)SelectObject(hdcBMP, hBMP);

        /* 不要になったウインドウのDCを解放 */
        ReleaseDC(hwnd, hdc);

        return 0;
    case WM_PAINT:
        {
  int i;
        hdc = BeginPaint(hwnd, &ps);

        ClearScreen();//画面を消す

        //PSET(160,120,0x00ff0000);//点を打つ
        //LINE(0,30,300,150,0x00ff0000);//線を描く

  Projection2DAxisFrom3DAxis(hdc);

  double Lx=0.0f;
  double Ly=0.0f;
  double Lz=-1.0f;


  //XYZ vpx=200,vpy=-120,vpz=100;
        for(i=0;i<MAXVTX;i+=1)
        {
            int i1=Poly[i][0];
            int i2=Poly[i][1];
            int i3=Poly[i][2];


            XYZ n=CalcCross(Pnt[i1],Pnt[i2],Pnt[i3]);

   Normalize(&n);


            XYZ light={0,0,-5};
            double lenlight=sqrt((double)light.x*light.x+light.y*light.y+light.z*light.z+0.1);

            light.x/=lenlight;
            light.y/=lenlight;
            light.z/=lenlight;

            double ViewCos;

   //-------------------------------------------------------------------------
            double v7=ViewCos=(light.x*n.x+light.y*n.y+light.z*n.z);

   //-------------------------------------------------------------------------
            //視線ベクトルと面ごとの法線ベクトルの内積
   //ViewCos<0
   if (ViewCos < 0)
   {
    TRIANGLE(Pnt2[Poly[i][0]].x,Pnt2[Poly[i][0]].y,Pnt2[Poly[i][1]].x,Pnt2[Poly[i][1]].y,
     Pnt2[Poly[i][2]].x,Pnt2[Poly[i][2]].y,RGB(0,0,v7*255));
   }
  }



        /* DIBSectionをDIBとして描画 */
            StretchDIBits(hdc, 0, 0, WIDTH*2, HEIGHT*2,
                0, 0, WIDTH, HEIGHT, lpPixel, &biInfo,
                DIB_RGB_COLORS,SRCCOPY);
            CalcFPS(hdc);

            EndPaint(hwnd, &ps);

            DeleteObject(SelectObject(hdc , GetStockObject(WHITE_BRUSH)));


        }
        return 0;

    case WM_DESTROY: /* ウインドウ破棄時 */

        SelectObject(hdcBMP, hBMPOLD);

        DeleteObject(hdcBMP);
        DeleteObject(hBMP);

        PostQuitMessage(0);

        return 0;

    }

    return DefWindowProc(hwnd, iMsg, wParam, lParam);

}
 006:objファイル読み込み
そろそろブログが重くなってきたみたいなので続きを別のココナラ内のブログに書きます。画像だけはチラッとおいておきます。
FlatShading(OBJ LOAD).png

サービス数40万件のスキルマーケット、あなたにぴったりのサービスを探す