OpenGL render obrazka SDL_image

0

Cześć! Uczę się openGLa od niedawna i przyszło mi się zmierzyć z renderem tekstury. Postanowiłem użyć do tego biblioteki SDL2. Problem polega na tym, że IDE nie pokazuje żadnych błędów związanych z kompilacją, kolor oraz kwadrat również się renderuje. Jedyne czego brakuje to obrazka, który został przeze mnie wybrany.

Do zrobienia VBO, kolorów oraz UV wykorzystałem struktury

struct GLTexture
{
	GLuint textureID;
	float width, height;
};

struct UV
{
	float u, v;
};

struct Position
{
	float x;
	float y;
};

struct ColorRGBA8
{
	ColorRGBA8() : r(0), g(0), b(0), a(0) { }
	ColorRGBA8(GLubyte R, GLubyte G, GLubyte B, GLubyte A) :
		r(R), g(G), b(B), a(A) { }
	GLubyte r;
	GLubyte g;
	GLubyte b;
	GLubyte a;
};

struct Vertex
{
	Position position;

	ColorRGBA8 color;

	UV uv;

	void setUV(float u, float v)
	{
		uv.u = u;
		uv.v = v;
	}

	void setPosition(float x, float y) 
	{
		position.x = x;
		position.y = y;
	}

	void setColor(GLubyte r, GLubyte g, GLubyte b, GLubyte a) 
	{
		color.r = r;
		color.g = g;
		color.b = b;
		color.a = a;
	}
};

w mainie ustawiłem wszystkie niezbędne rzeczy

int main(int arg, char*args[])
{
	SDL_Init(SDL_INIT_EVERYTHING);

	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

	SDL_Window* window = SDL_CreateWindow("window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
	SDL_GLContext context = SDL_GL_CreateContext(window);

	SDL_GL_SetSwapInterval(1);

	glewExperimental = true;

	GLenum result = glewInit();

	std::string vertex = loadFromFile("shader.vert.glsl");
	std::string frag = loadFromFile("shader.frag.glsl");

	GLuint shaderProgram = CreateShaderProgram(vertex.c_str(), frag.c_str());

	GLint uniformUV = glGetUniformLocation(shaderProgram, "mySampler");

	glUseProgram(shaderProgram);

        //Inicjuje kwadrat na pozycji x y i o szerokościach w i h
	InitVBO(1.0, 1.0, -2.0, -2.0);

        // Ładuje teksturę o nazwie ...
	GLTexture texture = LoadTexture("tekstura");

	std::cout << "IN MAIN ID :" << texture.textureID << std::endl;

	SDL_Event *event = new SDL_Event;
	bool quit = false;

	while (!quit && event->type != SDL_QUIT)
	{
		SDL_PollEvent(event);

		glClearDepthf(1.0);

		glUniform1i(uniformUV, 0);

		glClearColor(0.0, 0.0, 0.0, 1.0);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, texture.textureID);

		glDrawArrays(GL_TRIANGLES, 0, 6);

		glBindTexture(GL_TEXTURE_2D, 0);
		SDL_GL_SwapWindow(window);
	}

	glDisableVertexAttribArray(0);
	glDisableVertexAttribArray(1);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glDeleteBuffers(1, &vbo);
	glDeleteBuffers(1, &vao);

	return 0;
}

I metodą ładuje teksturę

GLTexture LoadTexture(std::string textureName)
{
	textureName = textureName + ".png";
	GLTexture texture{};
	SDL_Surface *surface = IMG_Load(textureName.c_str());

	texture.width = surface->w;
	texture.height = surface->h;

	glGenTextures(1, &texture.textureID);
	glBindTexture(GL_TEXTURE_2D, texture.textureID);

	int Format = GL_RGB;

	if (surface->format->BytesPerPixel == 4) {
		Format = GL_RGBA;
	}

	std::cout << "IN LOADTEXTURE ID :" << texture.textureID << std::endl;

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0, Format, GL_UNSIGNED_BYTE, surface->pixels);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

	glGenerateMipmap(GL_TEXTURE_2D);

	glBindTexture(GL_TEXTURE_2D, 0);

	return texture;
}

Inicjacja VBO


void InitVBO(float x, float y, float w, float h)
{
	Vertex vertex[6];

	vertex[0].setPosition(x + w, y + h);
	vertex[0].setUV(1.0f, 1.0f);

	vertex[1].setPosition(x, y + h);
	vertex[1].setUV(0.0f, 1.0f);

	vertex[2].setPosition(x, y);
	vertex[2].setUV(0.0f, 0.0f);
	
	vertex[3].setPosition(x, y);
	vertex[3].setUV(0.0f, 0.0f);

	vertex[4].setPosition(x + w, y);
	vertex[4].setUV(1.0f, 0.0f);

	vertex[5].setPosition(x + w, y + h);
	vertex[5].setUV(1.0f, 1.0f);

	for (int i = 0; i < 6; ++i)
	{
		vertex[i].setColor(255, 0, 255, 255);
	}

	vertex[1].setColor(0, 0, 255, 255);
	vertex[4].setColor(0, 255, 0, 255);

	glGenBuffers(1, &vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);

	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);

	glEnableVertexAttribArray(0);
	glEnableVertexAttribArray(1);

	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
	glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, color));
	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, uv));
}

Tak wyglądają shadery

Fragment

#version 330 core

out vec4 outColor;

in vec4 vertexColor;
in vec2 vertexPos;
in vec2 vertexUV;

uniform sampler2D mySampler;

void main()
{

	vec4 txt = texture(mySampler, vertexUV);
	outColor = txt * vertexColor;
}

I vertex

#version 330 core

layout (location = 0) in vec2 pos;
layout (location = 1) in vec4 col;
layout (location = 2) in vec2 uv;

out vec2 vertexPos;
out vec4 vertexColor;
out vec2 vertexUV;

void main()
{
	gl_Position = vec4(pos.xy, 0.0, 1.0);
	vertexColor = col;
	vertexPos = pos;
	vertexUV = uv;
}

Efekt końcowy

screenshot-20171223193259.png

obrazek który chciałbym załadować
screenshot-20171223193335.png

Próbowałem załatować jeszcze taki
screenshot-20171223193416.png

to efekt końcowy wyglądał tak, że w ogóle się nic nie pokazywało.

0
  1. Funkcja InitVBO: wywołanie glBindVertexArray(vao); powinno być przed glBindBuffer(GL_ARRAY_BUFFER, vbo); - z każdym vao stowarzyszone jest vbo, tak jakby vbo było częścią vao.
  2. Przed rysowanie kształtu (glDrawArrays(GL_TRIANGLES, 0, 6);) powinineś zbindować vao.
  3. funkcje glVertexAttribPointer - powinny być poprzedzone zbindowaniem shadera, ale to masz.
  4. rysujesz werteksy w porządku odwrotnym do ruchu wskazówek zegara, (domyślnie chyba jest wyłączone ukrywanie tylnej strony wielokąta, czyli obie strony są rysowane), ale jak nie to możesz ustawić sobie preferencje:
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
glFrontFace(GL_CW);

czyli nie rysujemy tylnej strony wielokąta, zaś przednią stroną jest ta rysowana zgodnie z ruchem wskazówek zegara (GL_CW)

0

Zmieniłem z tym vao. Rysowanie werteksów w innej kolejności raczej nie ma większego znaczenia. Stąd robiłem się uczyłem i kolesiowi działa. Korzystał z innego ładowania tekstur ale to chyba też nie powinno mieć większego znaczenia

0

Tutaj jedyne co widzę to nie zbindowałeś vao przy tworzeniu vbo, porównaj sobie dobrze kod ze źródła z którego się uczyłeś z tym co masz, to może być kwestia głupiego wywołania funkcji w nieodpowiedniej kolejności.

To mi się jeszcze wydaje wątpliwe:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0, Format, GL_UNSIGNED_BYTE, surface->pixels);

  • jeżeli obrazek ma format RGB to 3 parametr też powinien być GL_RGB
0
czaffik napisał(a):
  1. Funkcja InitVBO: wywołanie glBindVertexArray(vao); powinno być przed glBindBuffer(GL_ARRAY_BUFFER, vbo); - z każdym vao stowarzyszone jest vbo, tak jakby vbo było częścią vao

Przeglądam specyfikację i przeglądam i nigdzie nie widzę informacji żeby bindowanie VAO było z w jakikolwiek sposób powiązane z bindowaniem VBO.
Co by znaczyło że trzeba bindować jedno i drugie, w dowolnej kolejności.

funkcje glVertexAttribPointer - powinny być poprzedzone zbindowaniem shadera, ale to masz.

Tego też nie widzę. Zresztą przecież shader można podmieniać w locie, byłoby nielogiczne żeby glVertexAttribPointer był uzależniony od shadera.

0
czaffik napisał(a):

To mi się jeszcze wydaje wątpliwe:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0, Format, GL_UNSIGNED_BYTE, surface->pixels);

  • jeżeli obrazek ma format RGB to 3 parametr też powinien być GL_RGB

Niczego to niestety nie zmienia. Jutro przejrzę wszystko od początku i dam znać.

0

Nie wiem dlaczego ci nie działa — byłoby lepiej gdybyś dał cały program dający się skompilować — ale tu widzę wyciek pamięci:

    SDL_Surface *surface = IMG_Load(textureName.c_str());

Nigdzie nie zwalniasz tego obrazka. Po wykonaniu glTexImage2D nie jest ci już potrzebny.

0

https://github.com/DonTweaks/SDL_image-openGL/tree/master Mógłbyś mi też powiedzieć w jaki sposób mam taki błąd szukać. Debuger, debugerem, ale gdzie podstawić breakpoint w takim przypadku, gdzie uv jest ustawione, kolor działa, błędów w kompilacji nie pokazuje... Ustawiłem sobie breakpoint na początku maina i przejechałem po wszystkich funkcjach ale niczego złego nie mogłem zobaczyć. Chyba, że w inny sposób się szuka takich błędów.

0

@Azarien: Może i nie ma w specyfikacji takiej informacji, ale przed rysowaniem (glDrawArrays, glDrawElements) bindujesz vao (no chyba że to qt), a informacje o werteksach przekazujesz do vbo, więc wydaje się logiczne przypisać te vbo do jakiegoś vao.

Wywołanie glVertexAttribPointer też może być uzależnione bo robię to tak:

GLint value3 = glGetAttribLocation(program->getID(), "texcoord");
glEnableVertexAttribArray(value3);
glVertexAttribPointer(value3, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(6*sizeof(float)));

No chyba że jest tylko jeden shader, wtedy może nie trzeba pobierać wartości tylko iterować od zera, ale pewniej zrobić tak. (Chociaż jest layout location, może się mylę).

Pomylisz jedną małą rzecz i już ci się nie rysuje.

Odnośnie glVertexAttribArray:

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);

glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, color));
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, uv));

Czemu masz włączone tylko dwa atrybuty a przekazujesz trzy?

0

Hm... to mogło powodować błąd, ale już poprawiłem i to samo.

0

W takim razie: https://pastebin.com/79Vuq4CX - działa, tylko paskudnie wygląda :)
Zmodyfikowałem funkcję InitVBO

Jak zrobię tak:

glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));

glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, color));

glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, uv));

Też działa.

Jeszcze jedno, jakiego formatu obrazka używasz? Użyłem 512x512.

0

Dziękuje. Chodzi wszystko jak marzenie C:

1

Jeszcze jedna uwaga - takie coś:

    GLint uniformTime = glGetUniformLocation(shaderProgram, "time");
    GLint uniformUV = glGetUniformLocation(shaderProgram, "mySampler");

jest bardzo mylące. Jeszcze kilka tych uniformów i przestaniesz ogarniać który jest który.

Lepiej uniformy nazywać jednolicie w shaderze i w C, np. tak:

    GLint u_time = glGetUniformLocation(shaderProgram, "u_time");
    GLint u_mySampler = glGetUniformLocation(shaderProgram, "u_mySampler");

To samo się tyczy atrybutów:

layout (location = 0) in vec2 a_pos; // tak samo a_col, a_uv
...
GLint a_pos = glGetAttribLocation(shaderProgram, "a_pos"); // tak samo reszta

glEnableVertexAttribArray(a_pos);
glVertexAttribPointer(a_pos, ...);

tak łatwiej o porządek gdy się tych atrybutów i uniformów zrobi więcej.

1 użytkowników online, w tym zalogowanych: 0, gości: 1