Serwis Edukacyjny
Nauczycieli
w I-LO w Tarnowie

obrazek

Materiały dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

Autor artykułu: mgr Jerzy Wałaszek
Uaktualniono: 31.07.2022

©2025 mgr Jerzy Wałaszek
I LO w Tarnowie

Przezroczystość

SPIS TREŚCI
Podrozdziały

Kanał alfa

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 podrozdziału  do strony 

Wtapianie

Wtapianie (ang. blending) jest procesem łączenia pikseli źródłowych (np. ze stawianych punktów, rysowanych linii, itp.) z pikselami powierzchni rysunkowej (tekstura, powierzchnia graficzna). Standardowo, gdy biblioteka SDL 2 jest inicjalizowana, zostaje ustawiony tryb zastępowania, tzn. piksel na powierzchni rysunkowej otrzymuje kolor piksela źródłowego.

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:

SDL_BLENDMODE_NONE

Bez wtapiania. Piksel docelowy (tzn. znajdujący się na powierzchni rysunkowej) otrzymuje kolor oraz składową alfa piksela źródłowego (tzn. rysowanego przez daną operację rysunkową):

dstRGBA = srcRGBA

Jest to tryb standardowy.

SDL_BlendMode.line-4"> SDL_BLENDMODE_BLEND

Tryb przezroczystości. Kolor piksela docelowego po operacji graficznej zależy od parametru alfa piksela źródłowego, czyli tego, który jest stawiany na powierzchni rysunkowej, oraz od koloru piksela na tej powierzchni przed operacją.

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.

SDL_BLENDMODE_ADD

Wtapianie addytywne.  Kolor piksela źródłowego zostaje wymnożony przez jego parametr alfa i dodany do koloru piksela docelowego. Przezroczystość pikseli docelowych się nie zmienia:
α 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.

SDL_BLENDMODE_MOD

Modulacja koloru. Kolor wynikowy piksela docelowego powstaje przez wymnożenie koloru, który ten piksel miał przed operacją, przez kolor piksela źródłowego. Przezroczystość pikseli docelowych nie jest zmieniana.
β 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:

     

do podrozdziału  do strony 

Tryby wtapiania

Poniżej przedstawione są 4 proste programy z animacją. Kod programów różni się jedynie używanym trybem wtapiania. Najpierw w trybie SDL_BLENDMODE_NONE rysowane jest 100 losowych odcinków w różnych kolorach. Utworzą one tło. Następnie po obszarze okna przesuwane są dwa prostokąty o zmiennych kolorach wypełnienia, jeden poziomo, drugi pionowo. Prostokąty te są rysowane w wybranych trybach wtapiania.

Tryb SDL_BLENDMODE_NONE

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;
}

Tryb SDL_BLENDMODE_BLEND

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;
}

Tryb SDL_BLENDMODE_ADD

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;
}

Tryb SDL_BLENDMODE_MOD

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;
}

do podrozdziału  do strony 

Własny tryb wtapiania

W SDL 2 istnieje możliwość stworzenia własnego trybu wtapiania, jeśli żaden z dostępnych trybów nie jest odpowiedni.

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:

Tryb SDL_BLENDMODE_NONE

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)

Tryb SDL_BLENDMODE_BLEND

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)

Tryb SDL_BLENDMODE_ADD

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)

Tryb SDL_BLENDMODE_MOD

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;
}

do podrozdziału  do strony 

Podsumowanie

SDL_SetRenderDrawBlendMode(r,b) – ustawia tryb wtapiania
r – kontekst graficzny
b – nowy tryb wtapiania

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
SDL_ComposeCustomBlendMode(srccf,dstcf,cop,srcaf,dstaf,aop) – definiuje tryb wtapiania użytkownika
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.

do podrozdziału  do strony 

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: i-lo@eduinf.waw.pl
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.