Bài giảng Lập trình nâng cao: Đồ hoạ với SDL cung cấp cho người học các kiến thức: Thư viện SDL, xây dựng API vẽ, vẽ hình bằng bút vẽ, vẽ hình fractal. Mời các bạn cùng tham khảo nội dung chi tiết.
Trang 17&8 - Đồ hoạ với SDL
https://github.com/tqlong/advprogram
Trang 2● Vẽ hình fractal
Trang 3Đồ họa với SDL
● https://www.libsdl.org/
● Hướng dẫn: http://wiki.libsdl.org/FrontPage
● SDL có thể phát triển trò chơi chuyên nghiệp
● SDL dễ dàng kết nối với CodeBlocks
● SDL chạy trên nhiều nền tảng (Windows,
Linux, Android, iOS …)
Trang 4Lựa chọn thư viện
○ Thường cần thư viện (bên thứ 3 - third party library) ngoài tính
năng của ngôn ngữ và thư viện chuẩn của C++
○ https://en.wikipedia.org/wiki/List_of_game_engines
Trang 6Cài đặt SDL với CodeBlocks-MinGW
trong thư mục này có 4 thư mục bin, include, lib, share
mã nguồn project )
6 / 15
Trang 7Cấu hình CodeBlocks
Settings / Compiler
Trang 8Cấu hình CodeBlocks
Liên kết thư viện:
● Linker settings: -lmingw32 -lSDL2main -lSDL2
8 / 15
Trang 9Cấu hình CodeBlocks
Vị trí thư mục include và lib: Search directories | Compilers
Đường dẫn đến thư
mục chứa SDL
Trang 10Cấu hình CodeBlocks
Vị trí thư mục include và lib: Search directories | Linker
10 / 15
Trang 12Khởi tạo SDL
# include < iostream >
# include < SD L.h>
usin g n am esp ace std;
in t m ain(in t argc, char* argv[])
{
return ;
}
sử dụng thư viện SDL2
Trang 13Báo lỗi SDL
void logS D LError(std::ostream & os,
con st std::string & m sg, b ool fatal = false);
void logS D LError(std::ostream & os,
con st std::string & m sg, b ool fatal)
Trang 14Khởi tạo SDL
con st int SCREEN _W ID TH = 800;
con st int SCREEN _H EIG H T = 600;
con st string W IN D O W _TITLE = "An Im plem entation of Code.org Painter";
void in itS D L(SD L_W indow * & w indow , SD L_Renderer* & renderer);
Đại diện cho cửa sổ vẽ Đại diện cho bút vẽ
Trang 15Khởi tạo SDL
con st int SCREEN _W ID TH = 800;
con st int SCREEN _H EIG H T = 600;
con st string W IN D O W _TITLE = "An Im plem entation of Code.org Painter";
void in itS D L(SD L_W indow * & w indow , SD L_Renderer* & renderer);
void initS D L(SD L_W indow * & w indow , SD L_Renderer* & renderer)
{
if (SD L_Init(SD L_IN IT_EVERYTH IN G ) != 0)
logSD LError(std::cout, "SD L_Init", true );
w indow = SD L_CreateW indow (W IN D O W _TITLE.c_str(), SD L_W IN D O W PO S_CEN TERED ,
SD L_W IN D O W PO S_CEN TERED , SCREEN _W ID TH , SCREEN _H EIG H T, SD L_W IN D O W _SH O W N ); //w indow = SD L_CreateW indow (W IN D O W _TITLE.c_str(), SD L_W IN D O W PO S_CEN TERED ,
SD L_W IN D O W PO S_CEN TERED , SCREEN _W ID TH , SCREEN _H EIG H T,
SD L_W IN D O W _FU LLSCREEN _D ESKTO P);
if (w indow = = nullptr)
logSD LError(std::cout, "CreateW indow ", true );
renderer = SD L_CreateRenderer(w indow , -1, SD L_REN D ERER_ACCELERATED |
SD L_REN D ERER_PRESEN TVSYN C);
//SD L_Renderer *renderer = SD L_CreateSoftw areRenderer(SD L_G etW indow Surface(w indow ));
if (renderer = = nullptr)
logSD LError(std::cout, "CreateRenderer", true );
SD L_SetH int(SD L_H IN T_REN D ER_SCALE_Q U ALITY, "linear");
SD L_RenderSetLogicalSize(renderer, SCREEN _W ID TH , SCREEN _H EIG H T);
Mở cửa sổ vẽ theo kích thước
đã chọn
Lấy bút vẽ
Trang 16Giải phóng SDL
void q uitSD L(SD L_W indow * w indow , SD L_Renderer* renderer);
void q uitSD L(SD L_W indow * w indow , SD L_Renderer* renderer){
Trang 17Đợi 1 phím để thoát
void w aitU n tilK eyP ressed();
void w aitU n tilK eyP ressed()
{
SD L_Event e;
w h ile (true) {
if ( SD L_W aitEvent(& e) != 0 & &
(e.type = = SD L_KEYD O W N || e.type = = SD L_Q U IT) )
return;
SD L_D elay(100);
}
Xác định sự kiện bàn phím
Trang 18initSD L(w indow , renderer);
// Your draw ing code here
// use SD L_RenderPresent(renderer) to show it
w aitU ntilKeyPressed();
quitSD L(w indow , renderer);
return ;}
https://github.com/tqlong/advprogram/raw/6f01f8f6f96afe0aa9e107d65dcde17802f1e1e3/lec10-sdl/main.cpp
Trang 19Cửa sổ trắng,
ấn 1 phím bất
kỳ để thoát
Trang 20● Vẽ hình fractal
Trang 21Vẽ hình với SDL
● SDL đã cung cấp hàm
● Ta cũng có thể xây dựng thư viện riêng
○ Vẽ hình theo phong cách của riêng chúng ta
Trang 22Lớp Painter
● API vẽ ( Application Program Interface )
● Cách vẽ
độ), màu vẽ trắng, màu nền xanh
Trang 23void setP osition(f l oat x, f l oat y);
f l oat getX() con st { return x; }
f l oat getY() const { return y; }
void setAngle(f l oat angle);
f l oat getAngle() con st { return angle; }
in t getW idth() con st { return w idth; }
in t getH eight() con st { return height; }
void setColor(SD L_Color color);
SD L_Color getColor() const { return color; } void clearW ithBgColor(SD L_Color color);
SD L_Renderer* getRenderer() con st
{ return renderer; }
Trang 24Một số màu hay dùng
con st SD L_Color CYAN _CO LO R = {0, 255, 255};
con st SD L_Color BLU E_CO LO R = {0, 0, 255};
con st SD L_Color O RAN G E_CO LO R = {255, 165, 0};
con st SD L_Color YELLO W _CO LO R = {255, 255, 0};
con st SD L_Color LIM E_CO LO R = {0, 255, 0};
con st SD L_Color PU RPLE_CO LO R = {128, 0, 128};
con st SD L_Color RED _CO LO R = {255, 0, 0};
con st SD L_Color W H ITE_CO LO R = {255, 255, 255};
con st SD L_Color BLACK_CO LO R = {0, 0, 0};
con st SD L_Color G REEN _CO LO R = {0, 128, 0};
con st SD L_Color D EFAU LT_CO LO R = BLACK_CO LO R;
Nên đặt đoạn
mã này ở đâu ?
http://stackoverflow com/questions/23870 83/where-should-you- put-global-constants -in-a-c-program
Trang 25Lớp Painter: Hàm khởi tạo
Painter::Painter(SD L_W indow * w indow , SD L_Renderer* renderer_)
: renderer(renderer_)
{
SD L_RenderG etLogicalSize(renderer, & w idth, & height);
if (w idth = = 0 & & height = = 0)
SD L_G etW indow Size(w indow , & w idth, & height);
setPosition(w idth/2, height/2);
setAngle(0);
setColor(W H ITE_CO LO R);
clearW ithBgColor(BLU E_CO LO R);
Khởi tạo tọa độ, màu và hướng ban đầu của bút vẽ, tô nền bằng màu xanh
Lấy kích thước cửa sổ
Trang 26void Painter::setPosition(f l oat x, f l oat y)
SD L_SetRenderD raw Color(
renderer, color.r, color.g, color.b, 0);
vẽ và tô màu nền
Trang 27Đi tới vẽ đoạn thẳng
p ub lic:
// basic draw ing functions
void m oveForw ard(f l oat length);
void jum p Forw ard(f l oat length);
void Painter::m oveForw ard(f l oat length){
f l oat prevX = x, prevY = y;
jum pForw ard(length);
SD L_RenderD raw Line(renderer, (in t)prevX, (in t)prevY,
và vẽ đoạn thẳng
Trang 28Đi lùi, nhảy lùi
jum pForw ard(-length);
}
Trang 29Quay trái, quay phải
turnLeft(-angle);
}
Trang 30f l oat length = generateRandom N um ber() * M AX_LEN G TH ;
painter.m oveForw ard(length);
f l oat angle = generateRandom N um ber() * 360;
initSD L(w indow , renderer);
Painter painter(w indow , renderer); random W alk(painter);
SD L_RenderPresent(renderer);
}
Chọn độ dài bước và hướng đi ngẫu nhiên
Trang 32Chọn màu ngẫu nhiên
void Painter::setRandom Color()
}
Trang 34○ Đường thẳng, hình vuông, tam giác …
○ Phối hợp tạo thành các hình tuyệt đẹp
○ Vẽ ảnh JPG, PNG
● Vẽ hình fractal
Trang 3511: nhiều đường kẻ 12: hình thoi
13: nhiều hình tròn lồng nhau 14: bông tuyết tám cánh
15: đi dạo (ngẫu nhiên)
Trang 36Hình vuông
in t figN um ber = argc > 1 ? atoi(argv[1]) : 0;
sw itch (figN um ber)
Trang 40Tô kín tam giác
for (in t j = 0; j < 3; + + j) {
painter.turnLeft(120);
painter.m oveForw ard(size - i); }
painter.setPosition(curX, curY); painter.jum pBackw ard(i+ 1); }
painter.setPosition(curX, curY); break;
} // case 2
Vẽ các tam giác có kích thước nhỏ dần
Trang 42Hình bát giác
/* O ctagon */
case : painter.setPosition(350, 500);
painter.setColor(YELLO W _CO LO R);
for (in t i = 0; i < 8; + + i) {
painter.m oveForw ard(150);
painter.turnLeft(45);
} break;
Quay 45 độ 8 lần và
đi tới
Trang 44Sao năm cánh
/* Star of fives */
case : painter.setPosition(350, 200);
painter.setColor(YELLO W _CO LO R);
for (int i = 0; i < 5; + + i) {
painter.m oveForw ard(200);
Trang 46painter.m oveForw ard(150); painter.turnLeft(120);
}
b reak;
Vẽ 2 tam giác đều
Trang 48Nhím 8 gai
/* Eight lines crossing at center*/
case : painter.setColor(W H ITE_CO LO R);
for (in t i = 0; i < 8; + + i) {
painter.m oveForw ard(100);
painter.m oveBackw ard(100);
Trang 50Sáu hình vuông
/* Six squares */
case : for (in t i = 0; i < 6; + + i) {
for (in t j = 0; j < 4; + + j) {
painter.m oveForw ard(100);
painter.turnRight(90);
} painter.turnLeft(60);
}
b reak;
Vẽ một hình vuông
Quay 60 độ, vẽ tiếp hình vuông tất cả 6 lần
Trang 52Hình tròn
● Giải thuật vẽ hình tròn
■ https://en.wikipedia.org/wiki/Midpoint_circle_algo rithm
Trang 53Hình tròn
void Painter::createCircle(f l oat radius){
d oub le rad = (angle / 180) * M _PI;
in t centerX = x + cos(rad) * radius;
in t centerY = y - sin(rad) * radius;
in t dx = radius;
in t dy = 0;
in t err = 0;
Trang 54Hình tròn
void Painter::createCircle(f l oat radius)
{
d oub le rad = (angle / 180) * M _PI;
in t centerX = x + cos(rad) * radius;
in t centerY = y - sin(rad) * radius;
in t dx = radius;
in t dy = 0;
in t err = 0;
w h ile (dx > = dy) {
SD L_RenderD raw Point(renderer, centerX + dx, centerY + dy);
SD L_RenderD raw Point(renderer, centerX + dy, centerY + dx);
SD L_RenderD raw Point(renderer, centerX - dy, centerY + dx);
SD L_RenderD raw Point(renderer, centerX - dx, centerY + dy);
SD L_RenderD raw Point(renderer, centerX - dx, centerY - dy);
SD L_RenderD raw Point(renderer, centerX - dy, centerY - dx);
SD L_RenderD raw Point(renderer, centerX + dy, centerY - dx);
SD L_RenderD raw Point(renderer, centerX + dx, centerY - dy);
if (err < = 0) {
dy + = 1; err + = 2*dy + 1; }
if (err > 0) {
dx -= 1; err -= 2*dx + 1; }
} // w hile} // createCircle()
● Từ 1 điểm suy ra 7 điểm khác bằng tính đối xứng của hình tròn
● Tính điểm kế tiếp (tăng dy
hoặc giảm dx 1 điểm ảnh) bằng cách kiểm tra err
Chi tiết thuật toán
https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
Trang 5530 điểm ảnh
Trang 57Đi tới 50 điểm ảnh, xoay hướng 18 độ
18 x 20 lần = 360 độ
Trang 59{ //painter.random Color();
painter.createSquare(100); painter.turnRight(36);
}
b reak;
Trang 61painter.m oveForw ard(150);
painter.jum pBackw ard(150);
painter.turnRight(4);
}
b reak;
Trang 63Hình thoi
/* Pattern of Ten parallelogram s */
case 12: painter.setColor(W H ITE_CO LO R); for (in t i = 0; i < 10; + + i)
{ painter.createParallelogram (100); painter.turnRight(36);
Trang 65Nhiều hình tròn lồng nhau
/* Five and five cirles */
//*
case 13: painter.setColor(W H ITE_CO LO R);
painter.clearW ithBgColor(G REEN _CO LO R);
for (int i = 0; i < 5; + + i) {
painter.createCircle(100);
painter.createCircle(50);
painter.turnRight(72);
}
Trang 67painter.turnLeft(45);
painter.m oveForw ard(size);
painter.jum pBackw ard(size);
painter.turnRight(90);
painter.m oveForw ard(size);
painter.jum pBackw ard(size);
painter.turnLeft(45);
painter.m oveForw ard(size);
https://github.com/tqlong/advprogram/archive/3677695699840c851d6e22972eb4ff7353540e00.zip
Trang 69● Đọc ảnh vào SDL_Surface (bitmap)
● Chuyển sang SDL_Texture (phụ thuộc driver)
● Dùng SDL_RenderCopy() vẽ SDL_Texture
● Giải phóng SDL_Texture
Trang 70Chuẩn bị SDL_Texture
SD L_Texture* Painter::loadTexture( string path )
{
SD L_Texture* new Texture = N U LL;
SD L_Surface* loadedSurface = IM G _Load( path.c_str() );
Trang 71Vẽ ảnh
b ool Painter::createIm age( SD L_Texture* texture )
{
if( texture = = N U LL ) return false;
SD L_RenderCopy( renderer, texture, N U LL, N U LL );
return true;
case 16: {
if (argc < = 2) { cout < < "Please provide im age fi le path" < < endl; break;
} texture = painter.loadTexture(argv[2]);
painter.createIm age(texture);
} break;
Trang 73● Vẽ hình fractal
○ Kỹ thuật đệ quy
Trang 75● Nếu dãy phân kì thì c không thuộc tập hợp
○ Tô màu với số n mà |zn|2 = xn2 + yn2 ≥ 4 (phân kì)
● Nếu n > MAX_ITERATION, coi c thuộc tập
hợp (tô màu đen)
Trang 76Thuật toán điểm thoát (escape)
For each pixel (Px, Py) on the screen, do:
{
x0 = scaled x coordinate of pixel (scaled to lie in the Mandelbrot X scale (-2, 1.5))
y0 = scaled y coordinate of pixel (scaled to lie in the Mandelbrot Y scale (-2, 1.5))
Trang 77Chuyển đổi tọa độ
Tọa độ hiển thị tọa độ trên mặt phẳng phức ⇔
Trang 78void draw M andelb rot(Painter& painter, f l oat xm in, f l oat ym in, f l oat xm ax, f l oat ym ax)
{
int w idth = painter.getW idth(), height = painter.getH eight();
const int M AX_ITERATIO N = 1000;
for (int px = 0; px < w idth; px+ + ) {
for (int py = 0; py < painter.getH eight(); py+ + ) {
f l oat x0 = (f l oat)px / w idth * (xm ax-xm in) + xm in, x = 0;
f l oat y0 = (f l oat)py / height * (ym ax-ym in) + ym in, y = 0;
int iteration = 0;
w hile (x*x+ y*y < 2 & & iteration < M AX_ITERATIO N ) {
f l oat xtem p = x*x-y*y+ x0;
y = 2*x*y+ y0;
x = xtem p;
iteration+ + ;
}
SD L_Color color = iteration < M AX_ITERATIO N ?
PALLETTE[iteration % PALETTE_CO UN T] : BLACK_CO LO R;
Tính điểm thoát của
c = (x0,y0)
Chuyển đổi tọa độ từ (px,py) qua
(x0,y0)
Sử dụng
SDL_RenderDrawPoint(renderer, px, py)
cài đặt hàm này
Trang 79Bảng màu
http://stackoverflow.com/questions/16500656/which-color-gradient-is-used-to-color-mandel brot-in-wikipedia
con st SD L_Color PALLETTE[] = {
Trang 80https://github.com/tqlong/advprogram/archive/9d8e1a0d5aed0f94e1095d89813cf0b2ee99bb21.zip
Trang 81Zoom in
(0, 0, 2*0.5, 1.5*0.5) (2*0.17, 1.5*0.17, 2*0.25,1.5*0.25)
Trang 82Fractal đệ quy
● Khái niệm đệ quy trong toán học / lập trình
qua chính nó với tham số khác (thường nhỏ hơn)
○ Giai thừa: factorial(n) = n * factorial(n-1)
○ Fibonaci: fibo(n) = fibo(n-1) + fibo(n-2)
● Hình fractal đệ quy
○ http://natureofcode.com/book/chapter-8-fractals/
Trang 83Tính giai thừa
in t factorial(in t n) {
if (n = = 1) { return ; } else { return n * factorial(n-1);
}}
Điều kiện dừng đệ quy
Trang 84painter.jum pForw ard(radius*0.25f);
draw RecursiveCircle(painter, radius*0.75f);
painter.jum pBackw ard(radius*0.25f);
}
}
Điều kiện đệ quy (bán kính lớn hơn 2)
Trang 86Vẽ 2 hình tròn bên trái, phải
void d raw R ecursiveC ircle2(Painter& painter, f l oat radius)
{
painter.createCircle(radius);
if(radius > 2) {
painter.jum pBackw ard(radius / 2);
draw RecursiveCircle2(painter, radius / 2);
painter.jum pForw ard(radius * 2);
draw RecursiveCircle2(painter, radius / 2);
painter.jum pBackw ard(radius*3/ );
}
}
Trang 88Thêm 2 hình tròn trên, dưới
void d raw R ecursiveC ircle4(Painter& painter, f l oat radius)
draw RecursiveCircle4(painter, radius / 2);
painter.setPosition(x+ radius/2,y-radius);
draw RecursiveCircle4(painter, radius / 2);
painter.setPosition(x+ radius*3/ ,y);
draw RecursiveCircle4(painter, radius / 2);
painter.setPosition(x+ radius/2,y+ radius);
draw RecursiveCircle4(painter, radius / 2);
painter.setPosition(x,y);
}
}