三角形の走査変換もどき
三角形を塗ることができた。
とにかく実装してみたかった。今は反省している。
後処理が怪しい。
// main.cpp #include <stdlib.h> #include <stdio.h> #include <float.h> #include <math.h> #include <SDL/SDL.h> //----------------------------------------------- // 構造体の定義 //----------------------------------------------- struct Vector4 { float x, y, z, w; }; //----------------------------------------------- // 関数の宣言 //----------------------------------------------- void init(int w, int h, int bpp); void render(int w, int h); void putPixel(SDL_Surface * surface, int x, int y, Uint32 pixel); bool PollEvent(); //----------------------------------------------- // 大域変数 //----------------------------------------------- SDL_Surface* g_screen = NULL; //----------------------------------------------- // エントリーポイント //----------------------------------------------- int main(int argc, char** argv) { const int width = 640; const int height = 480; const int bpp = 8; init(width, height, bpp); // SDL 終了時のコールバックを設定 atexit(SDL_Quit); while(1) { // 描画 render(width, height); // 終了イベントがあれば終わる if(!PollEvent()) { break; } } return 0; } //----------------------------------------------- // 関数の実装 //----------------------------------------------- /*! @brief SDL 関係の初期化 */ void init(int w, int h, int bpp) { // SDL の初期化 if(SDL_Init(SDL_INIT_VIDEO) != 0) { fprintf(stderr, "Init Failed : %s", SDL_GetError()); exit(1); } // スクリーンのサイズ、BPPを設定 g_screen = SDL_SetVideoMode(w, h, bpp, SDL_HWSURFACE); if(g_screen == NULL) { fprintf(stderr, "640x480x8 Video mode failed: %s\n", SDL_GetError()); exit(1); } } /*! @brief サーフェイスの(x,y)にピクセルを設定 */ void putPixel(SDL_Surface * surface, int x, int y, Uint32 pixel) { int bpp = surface->format->BytesPerPixel; Uint8 *p = (Uint8*)surface->pixels + y * surface->pitch + x * bpp; switch(bpp) { case 1: *p = pixel; break; case 2: *(Uint16*)p = pixel; break; case 3: if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { p[0] = (pixel >> 16) & 0xff; p[1] = (pixel >> 8) & 0xff; p[2] = pixel & 0xff; } else { p[0] = pixel & 0xff; p[1] = (pixel >> 8) & 0xff; p[2] = (pixel >> 16) & 0xff; } break; case 4: *(Uint32*)p = pixel; break; } } /*! @brief 三角形を描く @note 走査変換は増分法を使用 */ void render(int w, int h) { // 黄色で塗る Uint32 yellow; yellow = SDL_MapRGB(g_screen->format, 0xff, 0xff, 0x00); // スクリーンのピクセルにアクセスするためにロック if(SDL_MUSTLOCK(g_screen)) { if(SDL_LockSurface(g_screen)<0) { fprintf(stderr, "Cant lock screen : %s\n", SDL_GetError()); exit(1); } } // 三角形の頂点 Vector4 t[3] = { {100.0, 200.0, 0.0, 1.0}, {300.0, 400.0, 0.0, 1.0}, {200.0, 100.0, 0.0, 1.0}, }; // x, y の最大値と最小値を保存する用 Vector4 mm = { 0.0, 0.0, 0.0, 0.0 }; // y 座標値でソート for(int i = 0; i < 3-1; ++i) { for(int j = 3-1; j > i; --j) { if(t[j-1].y > t[j].y) { mm = t[j]; t[j] = t[j-1]; t[j-1] = mm; } } } #if 0 for(int i = 0; i < 3; ++i) { printf("%d : (x, y)=(%lf, %lf)\n", i, t[i].x, t[i].y); } #endif // x のスパン保存 mm.x = t[0].x; mm.y = t[2].x; for(int i = 0; i < 3; ++i) { if(mm.x > t[i].x) { mm.x = t[i].x; } if(mm.y < t[i].x) { mm.y = t[i].x; } } // y のスパン保存 mm.z = t[0].y; mm.w = t[2].y; #if 0 printf("x : [%lf, %lf]\n", mm.x, mm.y); printf("y : [%lf, %lf]\n", mm.z, mm.w); #endif // 三角形を2分割した時にできる頂点 Vector4 divMid; divMid.y = t[1].y; divMid.x = (t[1].y-t[0].y)/(t[2].y-t[0].y)*(t[2].x-t[0].x) + t[0].x; #if 0 printf("divMid (%lf,%lf)\n", divMid.x, divMid.y); #endif //--------------------------------- // 上三角形 // 傾きがマイナスになるかプラスになるか Vector4* small; Vector4* large; if(t[1].x > divMid.x) { large = &t[1]; small = &divMid; } else { large = &divMid; small = &t[1]; } double m_nega = (small->x - t[0].x)/(small->y - t[0].y); double m_posi = (large->x - t[0].x)/(large->y - t[0].y); double min_x = t[0].x; double max_x = t[0].x; for(int y = static_cast<int>(mm.z); y < static_cast<int>(divMid.y); ++y) { // min_x += m_nega; // max_x += m_posi; for(int x = static_cast<int>(min_x); x < static_cast<int>(max_x); ++x) { putPixel(g_screen, x, y, yellow); } min_x += m_nega; max_x += m_posi; } //--------------------------------- // 下三角形 small = NULL; large = NULL; if(t[1].x > divMid.x) { large = &t[1]; small = &divMid; } else { large = &divMid; small = &t[1]; } m_nega = (t[2].x - small->x)/(t[2].y - small->y); m_posi = (t[2].x - large->x)/(t[2].y - large->y); for(int y = static_cast<int>(divMid.y); y < static_cast<int>(mm.w); ++y) { // min_x += m_nega; // max_x += m_posi; for(int x = static_cast<int>(min_x); x < static_cast<int>(max_x); ++x) { putPixel(g_screen, x, y, yellow); } min_x += m_nega; max_x += m_posi; } // サーフェイスのロックを解除 if(SDL_MUSTLOCK(g_screen)) { SDL_UnlockSurface(g_screen); } // スクリーンのアップデート SDL_UpdateRect(g_screen, 0, 0, 0, 0); } /*! @brief 終了イベント判定 */ bool PollEvent() { SDL_Event event; while(SDL_PollEvent(&event)) { if(event.type == SDL_QUIT) { return false; } } return true; }