obrót tekstury 2D softwarowy

obrót tekstury 2D softwarowy
wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

szukam algorytmu do obrotu tekstury 2D softwarowo bez utraty jakości na teksturze po obrocie bo np w OpenGl było by tak
glRotatef(angle, 0.0f, 0.0f, 1.0f);
może można było by jakąś macierzą?
EDIT: google AI pisze tak:

Algorytm obrotu tekstury 2D polega na zastosowaniu macierzy transformacji do współrzędnych każdego punktu tekstury, co pozwala na obrócenie jej wokół zadanego punktu (środka obrotu) o określony kąt. Podstawowe wzory na obrócenie punktu (x, y) do pozycji (x', y') to: x' = x * cos(θ) - y * sin(θ) i y' = x * sin(θ) + y * cos(θ), gdzie θ to kąt obrotu.
Kluczowe kroki algorytmu:

1. Określenie kąta obrotu (θ):
Wartość kąta, o który tekstura ma zostać obrócona. 
  1. Wybór punktu obrotu:
    Często jest to środek tekstury, ale może być dowolny inny punkt na płaszczyźnie.

  2. Obliczenie nowych współrzędnych:
    Dla każdego piksela (x, y) tekstury, jego nowe współrzędne (x', y') są obliczane za pomocą poniższych wzorów:

    x' = x * cos(θ) - y * sin(θ)

y' = x * sin(θ) + y * cos(θ)

  1. Utworzenie nowej tekstury:
    Piksele są umieszczane w ich nowych położeniach, tworząc obróconą teksturę.

Wykorzystanie w praktyce:
Ten algorytm jest fundamentalną transformacją w grafice komputerowej, używaną nie tylko do tekstur, ale także do:

Obracania obiektów 2D: W animacjach, grach czy interfejsach użytkownika. 

Przesuwania i skalowania: Wraz z translacją i skalowaniem, obrót stanowi podstawę do manipulowania obiektami na ekranie

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

sprawdzę ten wpis:
x' = x * cos(θ) - y * sin(θ)
y' = x * sin(θ) + y * cos(θ)
ciekawe jak to wyjdzie w praktyce

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 526
0

W SFML jest funkcja do rotacji sprajta. Jak koniecznie chcesz mieć dostęp do pikseli to konwertuj później sprajt na sf:: Image. Spróbuję później wkleić kod, bo sam też potrzebuję tej funkcji i muszę ją jakoś napisać

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

tak tBane możliwe, ale ja chcę ręcznie po to będzie wmontowane w mój DrawConsole gdzie wszystko liczy się ręczie

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

tylko nie wiem jak zacząć, bo normalną tekstury rysuje się od xs,ys do xe ye , po obrocie będzie zajmowało większą powierzchnię narożnikami, jeszcze jedno pytanie czy to jest liczone w radianach czy trzeba dopisać alpha*PI/180

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 526
0

do obrotu sprajta w SFML podaje się kąt w stopniach

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

pewnie tak, a na żywo .. ręcznie? wg tego wzoru co podałem? jak tu zrobić obrót jeśli brak punktu środka, a wzór tego nie podaje? ...

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 526
0

Mam dla Ciebie kod na ręczne przestawianie pikseli w sf::Image z użyciem setPixel. Ja jeszcze próbuję zrobić to shaderem. Domyślnie rotacja jest względem punktu sf::Vector(0,0) sprajta, jak chcesz zmienić położenie środka sprajta należy użyć funkcji setOrigin(x,y)

Kopiuj
#include <SFML/Graphics.hpp>
#include <cmath>

sf::Image rotateImage(const sf::Image& src, float angleDeg) {
    sf::Vector2u size = src.getSize();
    sf::Image dst;
    dst.create(size.x, size.y, sf::Color::Transparent);

    float angleRad = angleDeg * 3.14159265f / 180.f;

    // środek obrazu
    float cx = size.x / 2.f;
    float cy = size.y / 2.f;

    float cosA = std::cos(angleRad);
    float sinA = std::sin(angleRad);

    for (unsigned y = 0; y < size.y; y++) {
        for (unsigned x = 0; x < size.x; x++) {
            // współrzędne w układzie lokalnym względem środka
            float dx = x - cx;
            float dy = y - cy;

            // obrót w drugą stronę (mapowanie dst -> src)
            float srcX =  dx * cosA + dy * sinA + cx;
            float srcY = -dx * sinA + dy * cosA + cy;

            int ix = int(std::round(srcX));
            int iy = int(std::round(srcY));

            if (ix >= 0 && iy >= 0 && ix < (int)size.x && iy < (int)size.y) {
                dst.setPixel(x, y, src.getPixel(ix, iy));
            }
        }
    }

    return dst;
}
wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

szybki jesteś 😀 ja nawet nie zdążyłem zacząć, możesz trochę to rozpisać bez tej łaciny bo nie rozumiem za bardzo początku 😕
jak to napisać jeśli ma się tylko dostęp do tekstury przez getpixel(x,y,R,G,B) a wpisanie przez setpixel(x,y,R,G,B);

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 526
0

A z Shaderem to robi się to tak:
Shader:

Kopiuj
std::string rotation_shader_source = R"(
uniform sampler2D texture;
uniform float angle;

vec2 rotate(vec2 uv, vec2 pivot, float a) {
    float c = cos(a);
    float s = sin(a);
    mat2 R = mat2(c, -s,
                  s,  c);
    return (R * (uv - pivot)) + pivot;
}

void main() {
    vec2 uv = gl_TexCoord[0].xy;

    vec2 rotated_uv = rotate(uv, vec2(0.5, 0.5), angle);

    if (rotated_uv.x < 0.0 || rotated_uv.x > 1.0 ||
        rotated_uv.y < 0.0 || rotated_uv.y > 1.0) {
        gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
    } else {
        gl_FragColor = texture2D(texture, rotated_uv);
    }
}
)";

Funkcja rotująca:

Kopiuj

void set_rotation(sf::Image& image, float angle) {

    angle = angle;

    sf::Texture tex;
    tex.loadFromImage(image);

    sf::RenderTexture rtex;
    rtex.create(tex.getSize().x, tex.getSize().y);

    sf::Shader sh;
    sh.loadFromMemory(rotation_shader_source, sf::Shader::Fragment);
    sh.setUniform("angle", angle);

    sf::Sprite spr(tex);
    rtex.clear(sf::Color::Transparent);
    rtex.draw(spr, &sh);
    rtex.display();

    image = rtex.getTexture().copyToImage();
}

01.png02.png

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

u mnie jest taki blok:

Kopiuj
void DrawObiectImage(int x,int y,int nr)
{
	int w=bi[nr].w;
	int h=bi[nr].h;
	
	for(int py=0;py<h;py+=1)
	{
		for(int px=0;px<w;px+=1)
		{
				
			int index=(px*3+(h-py)*w*3);
			Color3iT(bi[nr].bitmapData[index],
        			bi[nr].bitmapData[index+1],
        			bi[nr].bitmapData[index+2]);	//pobranie koloru piksela z tablicy z obrazem 
			PutPixelT(px+x,py+y);// dokoanie wpisu koloru na podstawie Color3iT do buffora głównego do renderingu

			}
		}
	}
}

i co ja tu mogę zrobić? bo nie wiem jak zacząć 😕

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 526
0

Zobacz może to zadziała - nie mam dostępu do całego Twojego kodu więc ręcznie wstawiałem zmienne:

Kopiuj
void DrawObiectImage(int x, int y, int nr, float angle)
{
	// x,y - pos
	// nr - coś tam
	// angle - w kątach

	int w = bi[nr].w;
	int h = bi[nr].h;

	float angle = angle * 3.14159265f / 180.f;

	// środek obrazu
	float cx = w / 2.f;
	float cy = h / 2.f;

	float cosA = std::cos(angle);
	float sinA = std::sin(angle);

	for (int py = 0; py < h; py += 1)
	{
		for (int px = 0; px < w; px += 1)
		{
			// współrzędne w układzie lokalnym względem środka
			float dx = x - cx;
			float dy = y - cy;

			// obrót w drugą stronę (mapowanie dst -> src)
			float srcX = dx * cosA + dy * sinA + cx;
			float srcY = -dx * sinA + dy * cosA + cy;

			int ix = int(std::round(srcX));
			int iy = int(std::round(srcY));

			if (ix >= 0 && iy >= 0 && ix < w && iy < h) {
				int index = (ix * 3 + (h - iy) * w * 3);
				Color3iT(bi[nr].bitmapData[index],
					bi[nr].bitmapData[index + 1],
					bi[nr].bitmapData[index + 2]);	//pobranie koloru piksela z tablicy z obrazem 
				PutPixelT(px + x, py + y);// dokoanie wpisu koloru na podstawie Color3iT do buffora głównego do renderingu
			}
		}
	}
}
wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

screenshot-20250826134554.png nie działa .. coś jest źle, w ogóle nie wyświetla tekstury tylko ciemno szary kwadrat

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 526
0

Ale z programu nie wyrzuca- znaczy się coś trzeba poprawić.

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 526
1

Spróbuj tego rozwiązania - napisal je ChatGPT może zadziała.

Kopiuj
void DrawObiectImage(int x, int y, int nr, float angleDeg)
{
    int w = bi[nr].w;
    int h = bi[nr].h;
    int bpp = 3; // bit per pixel // zmień na 4 jeśli RGBA
    const unsigned char* src = bi[nr].bitmapData;

    float ang = angleDeg * 3.14159265358979323846f / 180.f; // kąt w radianach

    // środek obrazka w przestrzeni lokalnej [0..w) x [0..h)
    float cx = (w - 1) * 0.5f;
    float cy = (h - 1) * 0.5f;

    float cosA = std::cos(ang);
    float sinA = std::sin(ang);

    for (int py = 0; py < h; ++py)
    {
        for (int px = 0; px < w; ++px)
        {
            // współrzędne dst względem środka
            float dx = px - cx;
            float dy = py - cy;

            // mapowanie dst -> src (obrót o -ang)
            float srcX =  dx * cosA + dy * sinA + cx;
            float srcY = -dx * sinA + dy * cosA + cy;

            // najbliższy sąsiad
            int ix = (int)std::floor(srcX + 0.5f);
            int iy = (int)std::floor(srcY + 0.5f);

            if (ix >= 0 && iy >= 0 && ix < w && iy < h)
            {
                // Jeśli Twoje dane są bottom-up (np. surowy BMP), użyj:
                // int base = ((h - 1 - iy) * w + ix) * bpp;
                int base = (iy * w + ix) * bpp;

                unsigned char r = src[base + 0];
                unsigned char g = src[base + 1];
                unsigned char b = src[base + 2];

                // Jeśli to BGR (typowe dla BMP), zamień kolejność:
                // std::swap(r, b);

                Color3iT(r, g, b);          // ustaw kolor
                PutPixelT(x + px, y + py);  // zapisz piksel w buforze
            }
        }
    }
}
wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

teraz działa ale trzeba dodać dodatkowe wartości na nową szerokość i wysokość bo jest obcięte ale działa
screenshot-20250826140248.png

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 526
0
Kopiuj
newWidth = ceil(|w*cos(ang)| + |h*sin(ang)|)
newHeight = ceil(|w*sin(ang)| + |h*cos(ang)|)

Aby środek został w środku, potrzebujesz też przesunięcia środka:

Kopiuj
cx  = (w  - 1) / 2
cy  = (h  - 1) / 2
ncx = (newWidth - 1) / 2
ncy = (newHeight - 1) / 2
// offset, by “nowy środek” pokrył się ze starym:
offX = int(round(ncx - cx))
offY = int(round(ncy - cy))

Taki kod podał ChatGPT:

Kopiuj
void DrawObiectImage_Fit(int x, int y, int nr, float angleDeg)
{
    int w = bi[nr].w;
    int h = bi[nr].h;
    int bpp = 3;
    const unsigned char* src = bi[nr].bitmapData;

    float ang  = angleDeg * 3.14159265358979323846f / 180.f;
    float c = std::cos(ang);
    float s = std::sin(ang);

    // nowa obwiednia
    int newW = (int)std::ceil(std::fabs(w * c) + std::fabs(h * s));
    int newH = (int)std::ceil(std::fabs(w * s) + std::fabs(h * c));

    // środki
    float cx  = (w  - 1) * 0.5f;
    float cy  = (h  - 1) * 0.5f;
    float ncx = (newW - 1) * 0.5f;
    float ncy = (newH - 1) * 0.5f;

    // przesunięcie pozycji docelowej, aby obraz był wycentrowany względem (x,y)
    int offX = (int)std::round(ncx - cx);
    int offY = (int)std::round(ncy - cy);

    for (int py = 0; py < newH; ++py)
    {
        for (int px = 0; px < newW; ++px)
        {
            // współrzędne dst względem środka NOWEJ ramki
            float dx = px - ncx;
            float dy = py - ncy;

            // mapowanie dst -> src (obrót o -ang)
            float srcX =  dx * c + dy * s + cx;
            float srcY = -dx * s + dy * c + cy;

            int ix = (int)std::floor(srcX + 0.5f);
            int iy = (int)std::floor(srcY + 0.5f);

            if (ix >= 0 && iy >= 0 && ix < w && iy < h)
            {
                int base = (iy * w + ix) * bpp; // jeśli BMP bottom-up: (h-1-iy)*w + ix
                unsigned char r = src[base + 0];
                unsigned char g = src[base + 1];
                unsigned char b = src[base + 2];
                // jeśli BGR -> zamień r<->b

                Color3iT(r, g, b);
                PutPixelT(x + px - offX, y + py - offY);
            }
            // else: piksel poza źródłem — zostaw tło/nie rysuj
        }
    }
}
wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

screenshot-20250826141033.png
teraz już jest wszystko ok już nie przycina , dalej sobie już poradzę, dzięki tBane 😀

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

zobacz efekt tBane
DrawConsole.rar

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 526
0

Całkiem spoko :-) Tylko nie widzę sensu tego programu. To znaczy, co ten program ma do zaoferowania, po co ktoś miałby włączać więcej niż raz ten program. Dodaj może jakieś GUI do programu i obsługę tego GUI tzn jak np. kliknie sie w jakiś button to dzieje sie jakaś akcja.
Coś np takiego:
-przycisk uruchamiający grafikę
-przycisk do włączenia rotacji grafiki
-przycisk do wyświetlania FPS

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
1

pomyśle o tym GUI, kiedyś dawno temu pisałem własne GUI pod OpenGl :) była obsługa window, editbox, button,memo ...

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

mam funkcje która rysuje tylko fragment obrazka tzn wycinek i jak zrobić tutaj obrót fragmentu takiego wycinka tekstury:

Kopiuj
void DrawObiectImageCoord(int x,int y,int xs,int ys,int xe,int ye,int nr)
{	
	int w=bi[nr].w;
	int h=bi[nr].h;
		
		for(int py=ys;py<ye;py+=1)
		{
			for(int px=xs;px<xe;px+=1)
			{	
				int index=(px*3+(h-py)*w*3);
				Color3iT(bi[nr].bitmapData[index],
				bi[nr].bitmapData[index+1],
				bi[nr].bitmapData[index+2]);
					
				PutPixelT(px+x-xs,py+y-ys);	
			}
		}
}

tutaj efekt:
screenshot-20250830074112.png

tBane jak możesz to pomóż ...

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 526
0

@wilkwielki spróbuj to zrobić w ten sam sposób co ostatnio.

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 526
0

Może to zadziała ... Nie wiem bo nie mam gdzie i na czym skompilować programu... Tylko wydaje mi się, że rotacja nie będzie działać względem centralnego punktu obrazu.. Ale zobaczymy - weź to skompiluj i daj znać

Kopiuj
void DrawObiectImage_Fit(int x, int y, int nr, float angleDeg, int xs, int ys, int xe, int ye)
{
  // x - position x
  // y - position y
  // nr -
  // angleDeg - angle in degrees
  // xs = x start
  // ys - y start
  // xe - x end
  // ye - y end

    int w = bi[nr].w;
    int h = bi[nr].h;
    int bpp = 3;
    const unsigned char* src = bi[nr].bitmapData;

    float ang  = angleDeg * 3.14159265358979323846f / 180.f;
    float c = std::cos(ang);
    float s = std::sin(ang);

    // nowa obwiednia
    int newW = (int)std::ceil(std::fabs(w * c) + std::fabs(h * s));
    int newH = (int)std::ceil(std::fabs(w * s) + std::fabs(h * c));

    // środki
    float cx  = (w  - 1) * 0.5f;
    float cy  = (h  - 1) * 0.5f;
    float ncx = (newW - 1) * 0.5f;
    float ncy = (newH - 1) * 0.5f;

    // przesunięcie pozycji docelowej, aby obraz był wycentrowany względem (x,y)
    int offX = (int)std::round(ncx - cx);
    int offY = (int)std::round(ncy - cy);

      for (int py = ys; py < ye; ++py) // tu zmiana
    {
        for (int px = xs; px < xe; ++px) // tu zmiana
        {
            // współrzędne dst względem środka NOWEJ ramki
            float dx = px - ncx;
            float dy = py - ncy;

            // mapowanie dst -> src (obrót o -ang)
            float srcX =  dx * c + dy * s + cx;
            float srcY = -dx * s + dy * c + cy;

            int ix = (int)std::floor(srcX + 0.5f);
            int iy = (int)std::floor(srcY + 0.5f);

            if (ix >= 0 && iy >= 0 && ix < w && iy < h)
            {
                int base = (iy * w + ix) * bpp; // jeśli BMP bottom-up: (h-1-iy)*w + ix
                unsigned char r = src[base + 0];
                unsigned char g = src[base + 1];
                unsigned char b = src[base + 2];
                // jeśli BGR -> zamień r<->b

                Color3iT(r, g, b);
                PutPixelT(x + px - offX, y + py - offY);
            }
            // else: piksel poza źródłem — zostaw tło/nie rysuj
        }
    }
}
wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

moment ...

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

to jest funkcja w projekcie o nazwie: draw.DrawObiectImageCoordRotate(100, 100, id_ut, alpha, 50, 50,256,256);
DrawConsole.rar

wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

sprawdź sam a zobaczysz, że jeszcze jest źle i możesz poprawić sam ponieważ dałem ci cały projekt, jak poprawisz to wrzuć załącznik albo przez forum tez można ...

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 526
1

screenshot-20250830121419.png

Kopiuj
void DrawObiectImageCoordRotate(int x, int y, int nr, float angleDeg, int xs, int ys, int xe, int ye)
{
	int w = xe - xs;
	int h = ye - ys;
	if (w <= 0 || h <= 0) return;

	int bpp = 3;
	const unsigned char* src = bi[nr].bitmapData;

	int imgW = bi[nr].w;
	int imgH = bi[nr].h;

	float ang = angleDeg * 3.14159265358979323846f / 180.f;
	float c = std::cos(ang);
	float s = std::sin(ang);

	int newW = (int)std::ceil(std::fabs(w * c) + std::fabs(h * s));
	int newH = (int)std::ceil(std::fabs(w * s) + std::fabs(h * c));

	float cx = (w - 1) * 0.5f;
	float cy = (h - 1) * 0.5f;
	float ncx = (newW - 1) * 0.5f;
	float ncy = (newH - 1) * 0.5f;

	// gdzie postawić (0,0) nowej ramki, aby środek został w (x,y)
	int dstOriginX = x - (int)std::round(ncx - cx);
	int dstOriginY = y - (int)std::round(ncy - cy);

	for (int py = ys; py < ye; ++py)
	{
		for (int px = xs; px < xe; ++px)
		{
			// współrzędne lokalne w źródle (0..w-1 / 0..h-1)
			float lx = float(px - xs);
			float ly = float(py - ys);

			// --- ZMIANA: obrót do przodu (src -> dst) wokół środka źródła ---
			float dx = lx - cx;
			float dy = ly - cy;

			float rx = dx * c - dy * s;        // po obrocie
			float ry = dx * s + dy * c;

			int dstX = (int)std::floor(rx + ncx + 0.5f);
			int dstY = (int)std::floor(ry + ncy + 0.5f);
			// ----------------------------------------------------------------

			// odczyt piksela ze źródła
			int ix = (int)lx;
			int iy = (int)ly;
			if (ix < 0 || iy < 0 || ix >= w || iy >= h) continue;

			int sx = ix + xs;
			int sy = iy + ys;
			// jeśli BMP bottom-up: sy = (imgH - 1 - sy);

			int base = (sy * imgW + sx) * bpp;
			unsigned char r = src[base + 0];
			unsigned char g = src[base + 1];
			unsigned char b = src[base + 2];

			// --- ZMIANA: rysuj w obrócone współrzędne, nie w (lx-offX, ly-offY) ---
			Color3iT(r, g, b);
			PutPixelT(dstOriginX + dstX, dstOriginY + dstY);
		}
	}
}
wilkwielki
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 678
0

działa spoko, ale jest delikatna strata na jakości grafiki, jak się nie da lepiej trudno musi być 😀

tBane
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Poznań
  • Postów: 526
0

Można dodać efekt smooth tak jak ja to zrobiłem w swoim programie Anim Paint. Powinno wygładzić pixele i zaqpełnić luki w obrazie średnią z sąsiednich pikseli.

Zero Rotacji
screenshot-20250830121914.png

Rotacja bez efektu Smooth
screenshot-20250830122153.png

Rotacja z efektem Smooth
screenshot-20250830122122.png

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.