Serwis Edukacyjny Nauczycieli w I-LO w Tarnowie Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej
Autor artykułu: mgr Jerzy Wałaszek |
©2025 mgr Jerzy Wałaszek
|
SPIS TREŚCI |
Podrozdziały |
Na pewno zauważyłeś, że przy ustawianiu koloru rysowania podajesz 3 składowe: czerwoną, zieloną i niebieską, oraz dodatkowy parametr alfa, zwykle o wartości maksymalnej, czyli 255. W standardowym trybie rysowania, czyli takim, który jest ustawiany w czasie inicjalizacji SDL 2, parametr alfa nie jest wykorzystywany.
Jeśli jednak będzie wykorzystywany (o tym za chwilę), to służy on do określania poziomu przezroczystości koloru. Przezroczystość oznacza widoczność koloru, który posiadał piksel docelowy na powierzchni rysunkowej, po narysowaniu na nim nowego piksela w innym kolorze. Parametr alfa określa stopień przezroczystości rysowanego piksela. Przyjmuje on wartości od 0 (piksel zupełnie przezroczysty, a zatem niewidoczny) do 255 (piksel całkowicie nieprzezroczysty, zakryje piksel na powierzchni rysunkowej).
Wartości pośrednie powodują, iż po postawieniu piksela, wciąż w pewnym stopniu widoczny jest kolor poprzedniego piksela (zmieszany z kolorem nowego).
alfa = 0 | alfa = 127 | alfa = 255 |
Przezroczystości pikseli są pamiętane na teksturze, gdyż piksele mają zwykle format 32-bitowy, co daje 4 pola bitowe po 8 bitów każde. 3 pola są wykorzystane na składowe kolorów, a czwarte przechowuje informację o przezroczystości piksela, np. tak (rzeczywista kolejność pól może być inna, ponieważ zależy ona od używanego formatu pikseli):
bit | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
funkcja | alfa | składowa czerwona R | składowa zielona G | składowa niebieska B |
8-bitowe pole może zawierać 256 poziomów przezroczystości.
Wszystkie pola alfa powierzchni rysunkowej nazywamy kanałem alfa (ang. alphachannel). Informacja ta jest wykorzystywana przy kopiowaniu zawartości powierzchni rysunkowej na inną powierzchnię. Jeśli kanał alfa będzie uwzględniany, to piksele kopiowanej tekstury mogą posiadać różną przezroczystość i zostaną w różny sposób połączone z pikselami tekstury docelowej.
Załóżmy, iż na jednej teksturze masz obrazek piłki z cieniem:
Piksele tła (białe) mają ustawiony parametr alfa na 0, piksele piłki (czerwone) mają alfa 255, a piksele cienia (czarne) mają alfa 63.
Chcesz skopiować tę teksturę na inną, która zawiera rysunek kostki:
Załóżmy, że wszystkie piksele tej tekstury mają alfa równe 255, czyli są nieprzezroczyste. Kopiowanie chcesz wykonać, tak aby piłka znalazła się na górnej ściance kostki. Jeśli zrobisz to bez wykorzystywania kanału alfa, to otrzymasz:
+ | → |
Tło piłki przysłoniło część tekstury, na którą kopiowaliśmy obrazek.
Jeśli jednak tę samą operację wykonamy z uwzględnieniem kanału alfa, to tło piłki nie zmieni treści docelowej tekstury, a przez cień, który jest nieprzezroczysty w 25% (alfa = 63 to przezroczystość 75% lub nieprzezroczystość 25%) będzie widoczna górna ścianka kostki:
+ | → |
Do zmiany trybu wtapiania (ang. blending mode) istnieje w SDL 2 specjalna funkcja:
SDL_SetRenderDrawBlendMode(r,b)
r – kontekst graficzny
b – nowy tryb wtapiania
Parametr b przyjmuje jako wartość jedną z 4 stałych zdefiniowanych w enumeracji SDL_BlendMode. Wartości te są następujące:
dstRGBA = srcRGBA
Jest to tryb standardowy.
Stosowane tutaj są dwa wzory przeliczeniowe:
Oznaczmy:
α | – | parametr alfa piksela źródłowego |
β | – | parametr alfa piksela docelowego |
RGB | – | każda ze składowych koloru piksela |
src(RGB) | – | jedna z wybranych składowych koloru piksela źródłowego |
dst(RGB) | – | jedna z wybranych składowych koloru piksela docelowego |
Wzór pierwszy określa kolor piksela na płaszczyźnie po operacji. W miejsce RGB należy w nim wstawić odpowiednią składową koloru.
Przykład: niech piksel na płaszczyźnie będzie czerwony z alfa = 255 (zupełnie nieprzezroczysty):
dst(R) = 255
dst(G) = 0
dst(B) = 0
β = 255
a stawiany piksel niech będzie zielony z alfa = 127 (w połowie przezroczysty):
src(R) = 0
src(G) = 255
src(B) = 0
α = 127
Obliczamy składowe koloru piksela po operacji wg pierwszego wzoru:
Piksel wynikowy ma kolor:
Jak widzisz, nie jest to ani kolor czerwony, ani zielony. Kolory zmieszały się.
Drugi wzór określa wynikową przezroczystość piksela docelowego. Ma to znaczenie, gdy teksturę wynikową chciałbyś dalej wtapiać w inną teksturę. Dla naszego przykładu mamy:
Przezroczystość piksela docelowego się nie zmieniła. Piksele, które są nieprzezroczyste, dalej takimi pozostają – dlaczego, wyjaśnij to sobie sam jako proste ćwiczenie myślowe.
Obliczenia powyższe wykonuje bardzo szybko akcelerator graficzny w trakcie stawiania pikseli na teksturze.
α | – | parametr alfa piksela źródłowego |
β | – | parametr alfa piksela docelowego |
RGB | – | każda ze składowych koloru piksela |
src(RGB) | – | jedna z wybranych składowych koloru piksela źródłowego |
dst(RGB) | – | jedna z wybranych składowych koloru piksela docelowego |
Operacja prowadzi do rozjaśnienia kolorów pikseli na powierzchni rysunkowej. Jeśli suma da wartość większą od 255 (maksymalna wartość składowej koloru), to jest ustawiana na 255.
Przykład: niech piksel na płaszczyźnie będzie ciemno-czerwony:
dst(R) = 127
dst(G) = 0
dst(B) = 0
a stawiany piksel niech będzie szary z alfa = 127 (w połowie przezroczysty):
src(R) = 127
src(G) = 127
src(B) = 127
α = 127
Obliczamy składowe koloru piksela po operacji:
Piksel wynikowy ma kolor:
Piksel został wyraźnie rozjaśniony.
β | – | parametr alfa piksela docelowego |
RGB | – | każda ze składowych koloru piksela |
src(RGB) | – | jedna z wybranych składowych koloru piksela źródłowego |
dst(RGB) | – | jedna z wybranych składowych koloru piksela docelowego |
Operacja powoduje przyciemnienie kolorów pikseli docelowych.
Przykład: niech piksel na płaszczyźnie będzie czerwony:
dst(R) = 255
dst(G) = 0
dst(B) = 0
a stawiany piksel niech będzie szary:
src(R) = 127
src(G) = 127
src(B) = 127
Obliczamy składowe koloru piksela po operacji:
Piksel wynikowy ma kolor:
C++// Wtapianie 1 //------------ #include <SDL.h> #include <iostream> #include <cstdlib> #include <ctime> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; // Liczba odcinków const int N = 100; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Wtapianie: SDL_BLENDMODE_NONE", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Inicjujemy generator pseudolosowy srand(time(NULL)); // Generujemy odcinki int x1[N],y1[N],x2[N],y2[N],cr[N+2],cg[N+2],cb[N+2]; for(int i = 0; i < N+2; i++) { if(i < N) { x1[i] = rand() % W_W; y1[i] = rand() % W_H; x2[i] = rand() % W_W; y2[i] = rand() % W_H; } cr[i] = 1 + rand() % 255; cg[i] = 1 + rand() % 255; cb[i] = 1 + rand() % 255; } // Generujemy prostokąty SDL_Rect r1,r2; r1.x = r2.x = r1.y = r2.y = 0; r1.w = W_W; r1.h = W_H / 5; r2.w = W_W / 5; r2.h = W_H; int dx,dy; dx = dy = 1; // Animacja SDL_Event e; while(1) { // Kasujemy treść okna SDL_SetRenderDrawBlendMode(r,SDL_BLENDMODE_NONE); SDL_SetRenderDrawColor(r,0,0,0,255); SDL_RenderClear(r); // Rysujemy kolejne odcinki for(int i = 0; i < N; i++) { SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255); SDL_RenderDrawLine(r,x1[i],y1[i],x2[i],y2[i]); } // Zmieniamy tryb wtapiania SDL_SetRenderDrawBlendMode(r,SDL_BLENDMODE_NONE); // Rysujemy prostokąty SDL_SetRenderDrawColor(r,cr[N],cg[N],cb[N],127); SDL_RenderFillRect(r,&r1); SDL_SetRenderDrawColor(r,cr[N+1],cg[N+1],cb[N+1],127); SDL_RenderFillRect(r,&r2); // Modyfikujemy współrzędne wielokątów if((r1.y+dy < 0) || (r1.y+r1.h+dy >= W_H)) dy = -dy; r1.y += dy; if((r2.x+dx < 0) || (r2.x+r2.w+dx >= W_W)) dx = -dx; r2.x += dx; SDL_Delay(10); // Uaktualniamy okno SDL_RenderPresent(r); // Sprawdzamy, czy użytkownik nie zamyka programu if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break; } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
C++// Wtapianie 2 //------------ #include <SDL.h> #include <iostream> #include <cstdlib> #include <ctime> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; // Liczba odcinków const int N = 100; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Wtapianie: SDL_BLENDMODE_BLEND", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Inicjujemy generator pseudolosowy srand(time(NULL)); // Generujemy odcinki int x1[N],y1[N],x2[N],y2[N],cr[N+2],cg[N+2],cb[N+2]; for(int i = 0; i < N+2; i++) { if(i < N) { x1[i] = rand() % W_W; y1[i] = rand() % W_H; x2[i] = rand() % W_W; y2[i] = rand() % W_H; } cr[i] = 1 + rand() % 255; cg[i] = 1 + rand() % 255; cb[i] = 1 + rand() % 255; } // Generujemy prostokąty SDL_Rect r1,r2; r1.x = r2.x = r1.y = r2.y = 0; r1.w = W_W; r1.h = W_H / 5; r2.w = W_W / 5; r2.h = W_H; int dx,dy; dx = dy = 1; // Animacja SDL_Event e; while(1) { // Kasujemy treść okna SDL_SetRenderDrawBlendMode(r,SDL_BLENDMODE_NONE); SDL_SetRenderDrawColor(r,0,0,0,255); SDL_RenderClear(r); // Rysujemy kolejne odcinki for(int i = 0; i < N; i++) { SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255); SDL_RenderDrawLine(r,x1[i],y1[i],x2[i],y2[i]); } // Zmieniamy tryb wtapiania SDL_SetRenderDrawBlendMode(r,SDL_BLENDMODE_BLEND); // Rysujemy prostokąty SDL_SetRenderDrawColor(r,cr[N],cg[N],cb[N],127); SDL_RenderFillRect(r,&r1); SDL_SetRenderDrawColor(r,cr[N+1],cg[N+1],cb[N+1],127); SDL_RenderFillRect(r,&r2); // Modyfikujemy współrzędne wielokątów if((r1.y+dy < 0) || (r1.y+r1.h+dy >= W_H)) dy = -dy; r1.y += dy; if((r2.x+dx < 0) || (r2.x+r2.w+dx >= W_W)) dx = -dx; r2.x += dx; SDL_Delay(10); // Uaktualniamy okno SDL_RenderPresent(r); // Sprawdzamy, czy użytkownik nie zamyka programu if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break; } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
C++// Wtapianie 3 //------------ #include <SDL.h> #include <iostream> #include <cstdlib> #include <ctime> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; // Liczba odcinków const int N = 100; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Wtapianie: SDL_BLENDMODE_ADD", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Inicjujemy generator pseudolosowy srand(time(NULL)); // Generujemy odcinki int x1[N],y1[N],x2[N],y2[N],cr[N+2],cg[N+2],cb[N+2]; for(int i = 0; i < N+2; i++) { if(i < N) { x1[i] = rand() % W_W; y1[i] = rand() % W_H; x2[i] = rand() % W_W; y2[i] = rand() % W_H; } cr[i] = 1 + rand() % 255; cg[i] = 1 + rand() % 255; cb[i] = 1 + rand() % 255; } // Generujemy prostokąty SDL_Rect r1,r2; r1.x = r2.x = r1.y = r2.y = 0; r1.w = W_W; r1.h = W_H / 5; r2.w = W_W / 5; r2.h = W_H; int dx,dy; dx = dy = 1; // Animacja SDL_Event e; while(1) { // Kasujemy treść okna SDL_SetRenderDrawBlendMode(r,SDL_BLENDMODE_NONE); SDL_SetRenderDrawColor(r,0,0,0,255); SDL_RenderClear(r); // Rysujemy kolejne odcinki for(int i = 0; i < N; i++) { SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255); SDL_RenderDrawLine(r,x1[i],y1[i],x2[i],y2[i]); } // Zmieniamy tryb wtapiania SDL_SetRenderDrawBlendMode(r,SDL_BLENDMODE_ADD); // Rysujemy prostokąty SDL_SetRenderDrawColor(r,cr[N],cg[N],cb[N],127); SDL_RenderFillRect(r,&r1); SDL_SetRenderDrawColor(r,cr[N+1],cg[N+1],cb[N+1],127); SDL_RenderFillRect(r,&r2); // Modyfikujemy współrzędne wielokątów if((r1.y+dy < 0) || (r1.y+r1.h+dy >= W_H)) dy = -dy; r1.y += dy; if((r2.x+dx < 0) || (r2.x+r2.w+dx >= W_W)) dx = -dx; r2.x += dx; SDL_Delay(10); // Uaktualniamy okno SDL_RenderPresent(r); // Sprawdzamy, czy użytkownik nie zamyka programu if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break; } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
C++// Wtapianie 4 //------------ #include <SDL.h> #include <iostream> #include <cstdlib> #include <ctime> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; // Liczba odcinków const int N = 100; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Wtapianie: SDL_BLENDMODE_MOD", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Inicjujemy generator pseudolosowy srand(time(NULL)); // Generujemy odcinki int x1[N],y1[N],x2[N],y2[N],cr[N+2],cg[N+2],cb[N+2]; for(int i = 0; i < N+2; i++) { if(i < N) { x1[i] = rand() % W_W; y1[i] = rand() % W_H; x2[i] = rand() % W_W; y2[i] = rand() % W_H; } cr[i] = 1 + rand() % 255; cg[i] = 1 + rand() % 255; cb[i] = 1 + rand() % 255; } // Generujemy prostokąty SDL_Rect r1,r2; r1.x = r2.x = r1.y = r2.y = 0; r1.w = W_W; r1.h = W_H / 5; r2.w = W_W / 5; r2.h = W_H; int dx,dy; dx = dy = 1; // Animacja SDL_Event e; while(1) { // Kasujemy treść okna SDL_SetRenderDrawBlendMode(r,SDL_BLENDMODE_NONE); SDL_SetRenderDrawColor(r,0,0,0,255); SDL_RenderClear(r); // Rysujemy kolejne odcinki for(int i = 0; i < N; i++) { SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255); SDL_RenderDrawLine(r,x1[i],y1[i],x2[i],y2[i]); } // Zmieniamy tryb wtapiania SDL_SetRenderDrawBlendMode(r,SDL_BLENDMODE_MOD); // Rysujemy prostokąty SDL_SetRenderDrawColor(r,cr[N],cg[N],cb[N],127); SDL_RenderFillRect(r,&r1); SDL_SetRenderDrawColor(r,cr[N+1],cg[N+1],cb[N+1],127); SDL_RenderFillRect(r,&r2); // Modyfikujemy współrzędne wielokątów if((r1.y+dy < 0) || (r1.y+r1.h+dy >= W_H)) dy = -dy; r1.y += dy; if((r2.x+dx < 0) || (r2.x+r2.w+dx >= W_W)) dx = -dx; r2.x += dx; SDL_Delay(10); // Uaktualniamy okno SDL_RenderPresent(r); // Sprawdzamy, czy użytkownik nie zamyka programu if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break; } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Własny tryb wtapiania komponujesz za pomocą funkcji:
SDL_ComposeCustomBlendMode(srccf,dstcf,cop,srcaf,dstaf,aop)
srccf | – | współczynnik koloru dla piksela źródłowego (tego, który tworzy operacja graficzna) |
dstcf | – | współczynnik koloru dla piksela docelowego (tego, który znajduje się na powierzchni rysunkowej) |
cop | – | operacja wykonywana na przetworzonych kolorach pikseli źródłowego i docelowego |
srcaf | – | współczynnik alfa piksela źródłowego |
dstaf | – | współczynnik alfa piksela docelowego |
aop | – | operacja na przetworzonych parametrach alfa pikseli źródłowego i docelowego |
Funkcja zwraca numer trybu wtapiania, który możesz następnie użyć z funkcją SDL_SetRenderDrawBlendMode() ustawiającą tryb wtapiania dla operacji rysunkowych.
Współczynniki koloru składają się z 4 liczb, które są przemnażane przez składowe piksela (zawsze w kolejności: składowe koloru: czerwoną, zieloną, niebieską oraz składowa alfa). Operację mnożenia wykonuje akcelerator graficzny. Do funkcji nie są przekazywane 4 liczby dla każdego współczynnika, tylko jego identyfikator. Identyfikatory współczynników definiuje enumeracja SDL_BlendFactor. Składowe koloru oraz alfa są tutaj reprezentowane jako liczby z zakresu od 0 do 1. 1 oznacza maksymalną wartość składowej (w rzeczywistości jest to 255 dla 8 bitów). Z początku może być to mylące, jednak przeliczenia są wykonywane automatycznie i nie musisz sobie nimi zaprzątać głowy. W razie wątpliwości przeanalizuj podane przykłady. Identyfikatory współczynników są następujące:
Nazwa stałej enumeracji SDL_BlendFactor | Znaczenie stałej |
SDL_BLENDFACTOR_ZERO | 0, 0, 0, 0 |
SDL_BLENDFACTOR_ONE | 1, 1, 1, 1 |
SDL_BLENDFACTOR_SRC_COLOR | srcR, srcG, srcB, srcA |
SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR | 1-srcR, 1-srcG, 1-srcB, 1-srcA |
SDL_BLENDFACTOR_SRC_ALPHA | srcA, srcA, srcA, srcA |
SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA | 1-srcA, 1-srcA, 1-srcA, 1-srcA |
SDL_BLENDFACTOR_DST_COLOR | dstR, dstG, dstB, dstA |
SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR | 1-dstR, 1-dstG, 1-dstB, 1-dstA |
SDL_BLENDFACTOR_DST_ALPHA | dstA, dstA, dstA, dstA |
SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA | 1-dstA, 1-dstA, 1-dstA, 1-dstA |
Jak to działa? Załóżmy, że mamy piksel (będący wynikiem operacji rysunkowej, np. punkt do postawienia na powierzchni, lub piksel znajdujący się na powierzchni, który dana operacja chce zmodyfikować) o następujących składowych:
Oto, co dostaniemy w wyniku zastosowania dla tego piksela kilku współczynników:
SDL_BLENDFACTOR_ZERO
Ten współczynnik zeruje w wyniku wszystkie składowe, dla których jest stosowany
SDL_BLENDFACTOR_ONE
Ten współczynnik nie zmienia składowych.
SDL_BLENDFACTOR_SRC_COLOR
Ten współczynnik powoduje przemnożenie składowych piksela przez składowe piksela źródłowego. Pamiętaj, że składowe src(RGBA) są sprowadzane do zakresu od 0 do 1, np. przez podzielenie przez 255. Zatem, jeśli nasz piksel ma być postawiony na powierzchni (jest pikselem źródłowym), to otrzymamy:
Jeśli piksel jest pikselem docelowym, to w wyniku otrzymamy iloczyny składowych piksela źródłowego i docelowego.
SDL_BLENDFACTOR_SRC_ALPHA
W powyższych rachunkach zakładamy, że nasz piksel jest pikselem źródłowym. Jak widzisz ten współczynnik mnoży składowe piksela przez składową alfa piksela źródłowego.
Wracając do funkcji, dwa pierwsze parametry dotyczą przemnażania składowych koloru przez odpowiedni współczynnik:
srccf mnoży składowe koloru piksela źródłowego przez wybrany współczynnik (używane są 3 pierwsze liczby współczynnika kolejno dla składowych R, G i B). Składowa alfa nie jest zmieniana.
dstcf mnoży składowe koloru piksela docelowego przez wybrany współczynnik. Składowa alfa nie jest zmieniana.
Parametry te pozwalają ci przygotować składowe koloru obu pikseli do operacji, która da w wyniku składowe koloru piksela wynikowego, czyli tego, który trafi na powierzchnię rysunkową.
Kolejny parametr cop określa operację, która zostanie wykonana nad składowymi koloru obu pikseli po przemnożeniu przez współczynniki koloru. Operacje zdefiniowane są za pomocą enumeracji SDL_BlendOperation. Stałe tych operacji są następujące:
Nazwa stałej enumeracji SDL_BlendOperation | Znaczenie stałej |
SDL_BLENDOPERATION_ADD | operacja addytywna |
dst + src | |
SDL_BLENDOPERATION_SUBTRACT | operacja substraktywna |
dst - src | |
SDL_BLENDOPERATION_REV_SUBTRACT | odwrotna operacja substraktywna |
src - dst | |
SDL_BLENDOPERATION_MINIMUM | operacja minimum |
min(dst, src) | |
SDL_BLENDOPERATION_MAXIMUM | operacja maximum |
max(dst, src) |
Operacja wykonywana jest na kolejnych składowych obu pikseli. Na przykład SDL_BLENDOPERATION_ADD powoduje dodanie składowych koloru (przemnożonych przez odpowiednie współczynniki) obu pikseli, źródłowego i docelowego. W wyniku otrzymujemy składowe koloru piksela, który ostatecznie trafi na powierzchnię rysunkową. Jeśli w wyniku operacji otrzymamy wartość ujemną, to wynik jest ustawiany na 0. Tak samo, jeśli operacja da w wyniku wartość większą od 255, to wynik zostanie ustawiony na 255. Chodzi tutaj o to, iż pole składowej w pikselu jest 8-bitowe i nie może przechowywać wartości ujemnych, ani większych od 255.
Następne trzy parametry wykonują te same działania, lecz na składowych alfa pikseli:
srcaf mnoży składową alfa piksela źródłowego przez czwartą liczbę współczynnika.
dstaf mnoży składową alfa piksela źródłowego przez czwartą liczbę współczynnika.
Stosowane są tutaj takie same współczynniki, jak dla składowych koloru.
Również operacje aop nad składowymi alfa są te same, co dla składowych koloru.
Uwaga: nie wszystkie konteksty graficzne obsługują wszystkie operacje. Większość wykonuje tylko operację dodawania SDL_BLENDOPERATION_ADD ze współczynnikami. Wszystkie operacje wykonuje kontekst wykorzystujący Direct3D11.
Funkcja ta daje ci mnóstwo możliwości definiowania własnych trybów wtapiania. Na przykład standardowe tryby można zdefiniować tak:
SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE // składowe koloru piksela źródłowego zostają zachowane ,SDL_BLENDFACTOR_ZERO // składowe koloru piksela na powierzchni zostają wyzerowane ,SDL_BLENDOPERATION_ADD // suma daje składowe piksela źródłowego, które trafią na powierzchnię ,SDL_BLENDFACTOR_ZERO // zerujemy składową alfa piksela źródłowego ,SDL_BLENDFACTOR_ONE // zachowujemy składową alfa piksela docelowego ,SDL_BLENDOPERATION_ADD // suma da składową alfa piksela docelowego, czyli bez zmiany );
dst(RGB) = 1 x src(RGB) + 0 x dst(RGB)
dst(A) = 0 x src(A) + 1
x dst(A)
SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_SRC_ALPHA // składowe źródłowe koloru przemnażane przez składową źródłową alfa ,SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA // składowe docelowe koloru przemnażane przez 1 minus składowa źródłowa alfa ,SDL_BLENDOPERATION_ADD // suma daje piksel wynikowy, który trafi na powierzchnię ,SDL_BLENDFACTOR_ONE // zachowujemy składową źródłową alfa ,SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA // składowa docelowa alfa przemnożona przez 1 minus składowa źródłowa alfa ,SDL_BLENDOPERATION_ADD // suma da składową alfa piksela docelowego );
dst(RGB) = src(A) x src(RGB) + (1-src(A)) x dst(RGB)
dst(A) =
1 x src(A) + (1 - src(A)) x dst(A)
SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE // zachowujemy składowe źródłowe koloru ,SDL_BLENDFACTOR_ONE // zachowujemy składowe docelowe koloru ,SDL_BLENDOPERATION_ADD // sumujemy składowe koloru obu pikseli ,SDL_BLENDFACTOR_ZERO // zerujemy składową źródłową alfa ,SDL_BLENDFACTOR_ONE // zachowujemy składową docelową alfa ,SDL_BLENDOPERATION_ADD // suma pozostawi składową docelową alfa bez zmiany );
dst(RGB) = 1 x src(RGB) + 1 x dst(RGB)
dst(A) = 0 x src(A) + 1
x dst(A)
SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO // zerujemy składowe źródłowe koloru ,SDL_BLENDFACTOR_SRC_COLOR // składowe koloru obu pikseli mnożymy przez siebie ,SDL_BLENDOPERATION_ADD // suma daje iloczyn składowych źródłowych i docelowych koloru ,SDL_BLENDFACTOR_ZERO // zerujemy składową źródłową alfa ,SDL_BLENDFACTOR_ONE // zachowujemy składową docelową alfa ,SDL_BLENDOPERATION_ADD // suma pozostawi składową docelową alfa bez zmiany );
dst(RGB) = 0 x src(RGB) + src(RGB) x dst(RGB)
dst(A) = 0 x
src(A) + 1 x dst(A)
Teraz zaprojektujemy własny tryb wtapiania.
Wzór jest następujący:
dst(RGB) = 0 x src(RGB) + src(A) x dst(RGB)
dst(A) = 0 x
src(A) + 1 x dst(A)
SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ZERO // 0 x src(RGB) ,SDL_BLENDFACTOR_SRC_ALPHA // src(A) x dst(RGB) ,SDL_BLENDOPERATION_ADD // dst(RGB) = 0 x src(RGB) + src(A) x dst(RGB) ,SDL_BLENDFACTOR_ZERO // 0 x src(A) ,SDL_BLENDFACTOR_ONE // 1 x dst(A) ,SDL_BLENDOPERATION_ADD // dst(A) = 0 x src(A) + 1 x dst(B) );
Na piksel docelowy oddziałuje tylko składowa alfa piksela źródłowego. Poniższy program pokazuje sposób implementacji własnego trybu wtapiania.
C++// Wtapianie 4 //------------ #include <SDL.h> #include <iostream> #include <cstdlib> #include <ctime> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; // Liczba odcinków const int N = 100; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Wtapianie xxxx", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Inicjujemy generator pseudolosowy srand(time(NULL)); // Generujemy odcinki int x1[N],y1[N],x2[N],y2[N],cr[N+2],cg[N+2],cb[N+2]; for(int i = 0; i < N+2; i++) { if(i < N) { x1[i] = rand() % W_W; y1[i] = rand() % W_H; x2[i] = rand() % W_W; y2[i] = rand() % W_H; } cr[i] = 1 + rand() % 255; cg[i] = 1 + rand() % 255; cb[i] = 1 + rand() % 255; } // Generujemy prostokąty SDL_Rect r1,r2; r1.x = r2.x = r1.y = r2.y = 0; r1.w = W_W; r1.h = W_H / 5; r2.w = W_W / 5; r2.h = W_H; int dx,dy; dx = dy = 1; int a1,a2,da1,da2; a1 = rand() %256; // składowe alfa a2 = rand() %256; do da1 = -5 + rand() % 11; while(!da1); do da2 = -5 + rand() % 11; while(!da2); SDL_BlendMode bm = SDL_ComposeCustomBlendMode( SDL_BLENDFACTOR_ZERO // 0 x src(RGB) ,SDL_BLENDFACTOR_SRC_ALPHA // src(A) x dst(RGB) ,SDL_BLENDOPERATION_ADD // dst(RGB) = 0 x src(RGB) + src(A) x dst(RGB) ,SDL_BLENDFACTOR_ZERO // 0 x src(A) ,SDL_BLENDFACTOR_ONE // 1 x dst(A) ,SDL_BLENDOPERATION_ADD // dst(A) = 0 x src(A) + 1 x dst(B) ); // Animacja SDL_Event e; while(1) { // Kasujemy treść okna SDL_SetRenderDrawBlendMode(r,SDL_BLENDMODE_NONE); SDL_SetRenderDrawColor(r,0,0,0,255); SDL_RenderClear(r); // Rysujemy kolejne odcinki for(int i = 0; i < N; i++) { SDL_SetRenderDrawColor(r,cr[i],cg[i],cb[i],255); SDL_RenderDrawLine(r,x1[i],y1[i],x2[i],y2[i]); } // Zmieniamy tryb wtapiania SDL_SetRenderDrawBlendMode(r,bm); // Rysujemy prostokąty SDL_SetRenderDrawColor(r,cr[N],cg[N],cb[N],a1); SDL_RenderFillRect(r,&r1); SDL_SetRenderDrawColor(r,cr[N+1],cg[N+1],cb[N+1],a2); SDL_RenderFillRect(r,&r2); // Modyfikujemy współrzędne wielokątów if((r1.y+dy < 0) || (r1.y+r1.h+dy >= W_H)) dy = -dy; r1.y += dy; if((r2.x+dx < 0) || (r2.x+r2.w+dx >= W_W)) dx = -dx; r2.x += dx; // Modyfikujemy przezroczystości wielokątów if((a1+da1 < 0) || (a1+da1 > 255)) da1 = -da1; a1 += da1; if((a2+da2 < 0) || (a2+da2 > 255)) da2 = -da2; a2 += da2; SDL_Delay(10); // Uaktualniamy okno SDL_RenderPresent(r); // Sprawdzamy, czy użytkownik nie zamyka programu if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break; } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Tryby wtapiania:
SDL_BLENDMODE_NONE
|
bez wtapiania dstRGBA = srcRGBA |
SDL_BLENDMODE_BLEND
|
wtapianie alfa dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA)) dstA = srcA + (dstA * (1-srcA)) |
SDL_BLENDMODE_ADD
|
wtapianie addytywne dstRGB = (srcRGB * srcA) + dstRGB dstA = dstA |
SDL_BLENDMODE_MOD
|
modulacja koloru dstRGB = srcRGB * dstRGB dstA = dstA |
srccf | – | współczynnik koloru dla piksela źródłowego (tego, który tworzy operacja graficzna) |
dstcf | – | współczynnik koloru dla piksela docelowego (tego, który znajduje się na powierzchni rysunkowej) |
cop | – | operacja wykonywana na przetworzonych kolorach pikseli źródłowego i docelowego |
srcaf | – | współczynnik alfa piksela źródłowego |
dstaf | – | współczynnik alfa piksela docelowego |
aop | – | operacja na przetworzonych parametrach alfa pikseli źródłowego i docelowego |
Współczynniki koloru/alfa:
SDL_BLENDFACTOR_ZERO | 0, 0, 0, 0 |
SDL_BLENDFACTOR_ONE | 1, 1, 1, 1 |
SDL_BLENDFACTOR_SRC_COLOR | srcR, srcG, srcB, srcA |
SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR | 1-srcR, 1-srcG, 1-srcB, 1-srcA |
SDL_BLENDFACTOR_SRC_ALPHA | srcA, srcA, srcA, srcA |
SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA | 1-srcA, 1-srcA, 1-srcA, 1-srcA |
SDL_BLENDFACTOR_DST_COLOR | dstR, dstG, dstB, dstA |
SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR | 1-dstR, 1-dstG, 1-dstB, 1-dstA |
SDL_BLENDFACTOR_DST_ALPHA | dstA, dstA, dstA, dstA |
SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA | 1-dstA, 1-dstA, 1-dstA, 1-dstA |
Operacje:
SDL_BLENDOPERATION_ADD | operacja addytywna dst + src |
SDL_BLENDOPERATION_SUBTRACT | operacja substraktywna dst - src |
SDL_BLENDOPERATION_REV_SUBTRACT
|
odwrotna operacja substraktywna src - dst |
SDL_BLENDOPERATION_MINIMUM
|
operacja minimum min(dst, src) |
SDL_BLENDOPERATION_MAXIMUM
|
operacja maximum max(dst, src) |
Ograniczenia operacji wtapiania dla różnych kontekstów graficznych:
direct3d | Wspiera SDL_BLENDOPERATION_ADD ze wszystkimi współczynnikami |
direct3d11 | Wspiera wszystkie operacje ze wszystkimi współczynnikami. Jednakże niektóre współczynniki tworzą nieoczekiwane wyniki z SDL_BLENDOPERATION_MINIMUM i SDL_BLENDOPERATION_MAXIMUM. |
opengl | Wspiera operację SDL_BLENDOPERATION_ADD ze wszystkimi
współczynnikami. Wersje OpenGL 1.1, 1.2 i 1.3 nie działają poprawnie z SDL 2.0.6. |
opengles | Wspiera operację SDL_BLENDOPERATION_ADD ze wszystkimi
współczynnikami. Współczynniki koloru i przezroczystości muszą być takie
same. Specyficzne dla implementacji OpenGL ES 1: Może również wspierać SDL_BLENDOPERATION_SUBTRACT i SDL_BLENDOPERATION_REV_SUBTRACT. Może wspierać operacje koloru i przezroczystości różne od siebie. Może wspierać współczynniki koloru i przezroczystości różne od siebie. |
opengles2 | Wspiera operacje SDL_BLENDOPERATION_ADD, SDL_BLENDOPERATION_SUBTRACT, SDL_BLENDOPERATION_REV_SUBTRACT ze wszystkimi współczynnikami. |
psp | Nie obsługuje. |
software | Nie obsługuje. |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2025 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.