Sprite batching problem

Started by
8 comments, last by Green_Baron 4 years, 7 months ago

Hello! I need some help!

I have written sprite batching for my engine. But textures dont appear on the screen. Instead I can see only white squares. Can you solve the problem, pls?

There are my shaders and source code of sprite batching:

Vertex shader:


#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
layout (location = 2) in vec2 texCoord;

out vec3 ourColor;
out vec2 TexCoord;

void main()
{
    gl_Position = vec4(position, 1.0);
    ourColor = color;
    TexCoord = texCoord;
}

Fragment shader:


#version 330 core
in vec3 ourColor;
in vec2 TexCoord;

out vec4 color;

uniform sampler2D ourTexture;

void main()
{
    vec2 _texCoord = vec2(TexCoord.x, -TexCoord.y);
    color = texture(ourTexture, _texCoord);
}

SpriteBatch.h:


#pragma once

#include "ShaderProgram.h"
#include "Vertex.h"
#include "TextureManager.h"
#include <GL/glew.h>
#include <vector>

class Rect
{
public:
	GLfloat x, y, z, w, h;
};

class SpriteBatch
{
	class RenderBatch
	{
	public:
		GLuint offset;
		GLuint numSprites;
		GLuint textureID;
		RenderBatch() : offset(0), numSprites(0), textureID(0) {}
		RenderBatch(GLuint Offset, GLuint NumSprites, GLuint TextureID) : offset(Offset), numSprites(NumSprites), textureID(TextureID) {}
	};

private:
	GLuint _vao;
	GLuint _vbo;
	GLuint _ebo;
	
	size_t _sizeOfBuffers;
	size_t _numSprites;
	size_t _batchIterator;

	std::vector<Vertex> _vertices;
	std::vector<GLuint> _indices;
	std::vector<RenderBatch> _spriteBatch;
public:
	SpriteBatch();
	
	void init(size_t sizeOfBuffers = 5);
	void draw(const Rect& dstRect, const Rect& srcRect, const Texture& texture, const Color& color = { 1.0f, 1.0f, 1.0f });
	void render();
};

SpriteBatch.cpp:


#include "SpriteBatch.h"
#include <iostream>
#include "DebugInfo.h"

/*	TODO : */
/*
	sort it by z-value or use pojection
*/

SpriteBatch::SpriteBatch() :
	_vertices(0),
	_indices(0),
	_spriteBatch(1),
	_numSprites(0),
	_sizeOfBuffers(0),
	_batchIterator(0),
	_vao(0),
	_vbo(0),
	_ebo(0)
{
	_spriteBatch[0].textureID = 0;
}

void SpriteBatch::init(size_t sizeOfBuffers)
{
	glGenVertexArrays(1, &_vao);
	glGenBuffers(1, &_vbo);
	glGenBuffers(1, &_ebo);

	glBindVertexArray(_vao);

	_sizeOfBuffers = sizeOfBuffers;
	glBindBuffer(GL_ARRAY_BUFFER, _vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeOfBuffers * 4 * sizeof(Vertex), nullptr, GL_DYNAMIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeOfBuffers * 6 * sizeof(GLuint), nullptr, GL_DYNAMIC_DRAW);

	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
	glEnableVertexAttribArray(2);

	glBindVertexArray(0);
}

void SpriteBatch::draw(const Rect& dstRect, const Rect& srcRect, const Texture& texture, const Color& color)
{
	if (_spriteBatch[_batchIterator].textureID != texture.textureID)
	{
		if (_batchIterator + 1 == _spriteBatch.size())
			_spriteBatch.emplace_back(_spriteBatch[_batchIterator].offset + _spriteBatch[_batchIterator].numSprites, 1, texture.textureID);
		++_batchIterator;
	}
	else
	{
		_spriteBatch[_batchIterator].numSprites++;
	}
		
	if (_numSprites * 4 >= _vertices.size())
	{
		_vertices.resize(_vertices.size() + 4);
		_indices.resize(_indices.size() + 6);
	}
	
	// TOP RIGHT
	_vertices[_numSprites * 4].position = { dstRect.x + dstRect.w, dstRect.y, dstRect.z };
	_vertices[_numSprites * 4].color = color;
	_vertices[_numSprites * 4].uv = { srcRect.x + srcRect.w, srcRect.y };
	// BOT RIGHT
	_vertices[_numSprites * 4 + 1].position = { dstRect.x + dstRect.w, dstRect.y - dstRect.h, dstRect.z };
	_vertices[_numSprites * 4 + 1].color = color;
	_vertices[_numSprites * 4 + 1].uv = { srcRect.x + srcRect.w, srcRect.y + srcRect.h };
	// BOT LEFT
	_vertices[_numSprites * 4 + 2].position = { dstRect.x, dstRect.y - dstRect.h, dstRect.z };
	_vertices[_numSprites * 4 + 2].color = color;
	_vertices[_numSprites * 4 + 2].uv = { srcRect.x, srcRect.y + srcRect.h };
	// TOP LEFT
	_vertices[_numSprites * 4 + 3].position = { dstRect.x, dstRect.y, dstRect.z };
	_vertices[_numSprites * 4 + 3].color = color;
	_vertices[_numSprites * 4 + 3].uv = { srcRect.x, srcRect.y };

	_indices[_numSprites * 6] = _numSprites * 4;
	_indices[_numSprites * 6 + 1] = _numSprites * 4 + 1;
	_indices[_numSprites * 6 + 2] = _numSprites * 4 + 3;
	_indices[_numSprites * 6 + 3] = _numSprites * 4 + 1;
	_indices[_numSprites * 6 + 4] = _numSprites * 4 + 2;
	_indices[_numSprites * 6 + 5] = _numSprites * 4 + 3;
	
	++_numSprites;
}

void SpriteBatch::render()
{
	glBindVertexArray(_vao);
	
	glBufferSubData(GL_ARRAY_BUFFER, 0, _numSprites * 4 * sizeof(Vertex), _vertices.data());
	glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, _numSprites * 6 * sizeof(GLuint), _indices.data());

#ifdef DEBUG
#define DELAY_TIME 5000
	static Uint32 lastTick = SDL_GetTicks();
	if (SDL_GetTicks() - lastTick > DELAY_TIME && _vertices.size() / 4 > _sizeOfBuffers)
	{
		DebugInfo::printWarning("Not enough memory in GPU");
		lastTick = SDL_GetTicks();
	}	
#endif // DEBUG


	for (size_t i = 1; i <= _batchIterator; ++i)
	{
		glBindTexture(GL_TEXTURE_2D, _spriteBatch[i].textureID);
		glDrawElements(GL_TRIANGLES, _spriteBatch[i].numSprites * 6, GL_UNSIGNED_INT, (GLvoid*)(_spriteBatch[i].offset * 6 * sizeof(GLuint)));
	}
	
	_batchIterator = 0;
	_numSprites = 0;
	
	glBindVertexArray(0);
}

fragment2.shader

vertex1.shader

SpriteBatch.cpp

SpriteBatch.h

Advertisement
11 minutes ago, Loins_Jr said:

Can you solve the problem, pls?

It's likely that someone can help you solve the problem, but could you paste your code into a post so that it's easier to examine?

Guys, I need help)

22 minutes ago, Loins_Jr said:

Guys, I need help)

If you're on a deadline of some sort, a discussion forum might not be the best way to resolve your issue.

Just to be safe, let me ask, is this for homework of some sort?

4 minutes ago, Zakwayda said:

If you're on a deadline of some sort, a discussion forum might not be the best way to resolve your issue.

Just to be safe, let me ask, is this for homework of some sort?

No, it is just my hobby. And i just want to solve this really unusualproblem. I cant understand what is going wrong here. And I spent a few days to try to solve it. This is why I am here

6 minutes ago, Loins_Jr said:

No, it is just my hobby. And i just want to solve this really unusualproblem. I cant understand what is going wrong here. And I spent a few days to try to solve it. This is why I am here

I see. That's a fair amount of code to look through, but irrespective of that, I think there's code missing that we'd have to see anyway. For example, I don't see anything having to do with program or texture creation or setup (presumably that happens elsewhere).

In any case, there's probably little point in implementing something like sprite batching until you get the basics working. So, what I'd recommend is to write the simplest program you can that renders a single textured quad. There are plenty of tutorials that cover that material, so it should be fairly easy to find step-by-step instructions that will cover all the necessary steps. Once you have that working, you can move back to what you're doing currently, and if you find things aren't working, you can refer back to the simple example to see if you've missed anything, and/or post back here with your revised code.

Just now, Zakwayda said:

I see. That's a fair amount of code to look through, but irrespective of that, I think there's code missing that we'd have to see anyway. For example, I don't see anything having to do with program or texture creation or setup (presumably that happens elsewhere).

In any case, there's probably little point in implementing something like sprite batching until you get the basics working. So, what I'd recommend is to write the simplest program you can that renders a single textured quad. There are plenty of tutorials that cover that material, so it should be fairly easy to find step-by-step instructions that will cover all the necessary steps. Once you have that working, you can move back to what you're doing currently, and if you find things aren't working, you can refer back to the simple example to see if you've missed anything, and/or post back here with your revised code.

I did. And it worked perfectly: I have got my textured square. So texture loader and other stuff work perfectly. The problem is situated in this code.

Maybe the error is obvious in what you've posted, but it's more than I'm up to analyzing right now.

Maybe someone else will jump in, but meanwhile I'll offer some more advice.

Try calling glGetError(), if you're not already doing so, in order to catch any obvious errors. I also noticed you're negating the texture coordinate y values - you might double-check that that's what you want and that your texture parameters are set up appropriately for it. (Although unless your texture has some white in it, it seems more likely that texturing isn't working at all.)

Did you set the sampler2D uniform?

Anyway, this is a good exercise in debugging, I think. You said you successfully implemented rendering a single textured quad, so obviously there's some disconnect between that code and your current code - some step you've left out or something you've done differently. I'd suggest a careful analysis and comparison (perhaps just rendering a single sprite rather than many for the time being) to try to find where the disconnect is.

I'll have to leave it there for now, but maybe something there will help get you pointed in the right direction.

Welcome ?

Can't help in detail because i use opengl 4 and there is missing info about how the textures are loaded and bound, but maybe i can utter a few ideas from a short overfly:

Where do you set the uniforms/activate the texture unit (i think that's the way opengl 2 did it :-)) ?

Where do you store the indices into the element buffer ?

Why is there an offset into the element buffer when it appears to only (possibly correctly) hold indices for one quad ?

Check the winding order and the face culling.

Opengl 4 offers a debug context that issues hints, warnings and errors that offer more insight. Also OpenGL 3.x offers draw calls for batching where one can draw data from a complex buffer with one draw call. Just hinting ...

You say a single texture is painted but not the whole batch. There must be a breaking difference ...

This topic is closed to new replies.

Advertisement