Advanced C++/Graphics Tutorial 6: VBO, Sprite, and NDC!

preview_player
Показать описание

ATTENTION: Michiel De Cuyper was kind enough to point out that while NVIDIA cards will show a white box, other graphics cards may not be able to render properly without shaders. So don't be alarmed, we will be learning to use shaders in the next two tutorials!

In this tutorial we learn about Vertex Buffer Objects and Normalized Device Coordinates, and we create our basic Sprite Class!

Рекомендации по теме
Комментарии
Автор

MakingGamesWithBen awesome series dude, really easy to follow.

For anyone writing this engine on a Mac, there's a good chance that the square won't render for you like it didn't for me. I don't know if this is the proper way of fixing it because I'm just beginning to learn this stuff, but it fixed the GL errors for me and rendered the square.

It took me all of Xmas break of googling to finally figure it out haha. Anyway, come back to this comment after you watch the next two tutorials and you have the GLSLProgram class.

Ok so you're probably getting an error saying that the shader version is not supported, so the first thing you need to do is to set OpenGL Context into core profile mode. That basically means you're going to need shaders to render anything, and the fixed pipeline is not supported.

In MainGame.cpp, inside initSystems(), after SDL_Init() write:
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

Then go to Sprite.h and add a private variable called _vertexArrayObjectID of type GLuint.

Then go to Sprite.cpp, and add a new header:
#include <OpenGL/gl3.h>

You need gl3.h because we need to generate a vertex array object(VAO). It turns out that in core profile mode, we need a VAO in order to draw things. Now, without gl3.h, you have access to two functions and glBindVertexArraysAPPLE() which, in my case, return 0 when generating, and fail at binding. But gl3.h gives you access to glGenVertexArrays() and glBindVertexArrays() which give a proper ID and succeed at binding (in my case).

In the Sprite constructor initialize that new variable to 0. In init() right before you generate the vertexBufferID, write:
if (!_vertexArrayObjectID) {
   glGenVertexArrays(1, &_vertexArrayObjectID);
}

At the end of init() write:


If you've watched the next two tutorials, then you should have the GLSLProgram class and it should already be in use in MainGame.cpp. If that is the case, then you should be able to see the square now. In any case, your draw() function in MainGame.cpp should look like this:
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
_colorProgram.use();    
_testSprite.draw();    
_colorProgram.unuse();
SDL_GL_SwapWindow(_window);

The _colorProgram is important. Without it, you won't be able to render the square!

I hope that helps. Again, I'm just learning OpenGL myself so this could be really wrong. It did however allow me to render the square. Here are pasties of my Sprite and MainGame classes in case I may have missed something:



MakingGamesWithBen again, these tutorials are incredible. Thank you so much.

figgleforth
Автор

"When in doubt, use triangles." Spoken like a true master :D

davislast
Автор

For the people that did not understand the part where Ben wrote "glewExperimental = true", that mean you are telling to GLEW to use the more modern techniques to OpenGL. If you leave as default (false) might cause issues when you try to use the new core-profile of OpenGL (the new version of OpenGL).

LuizHeringerIA
Автор

I like what you're doing man and I really want to know more, i just wish i can everything single detail in my head

andyessien
Автор

Yes after two days of searching for solution to my problem I have finally fixed it !

judewoolls
Автор

@MakingGamesWithBen Cool to see you working at Guildwars studio! Good luck and thank you for sharing with us along the way.

gercovanwoudenbergh
Автор

I love your tutorials. I learned a lot of things, and some difficult tasks became easier because of you ! Thank you very much !

nyu
Автор

Great video!

Here are a couple of corrections.

(1) OpenGL immediate mode most certainly does not incur a call to the GPU on every glVertex() or other call.  This behavior is driver specific, but it almost certainly buffers data in the driver until the next call that causes a flush, or at least until the next vertical refresh.

That does not diminish your point: there certainly are more CPU->GPU calls using immediate mode, and there is a lot more bus bandwidth used.  Modern (buffered) OpenGL is the only way to get top performance (and all cards support it).

(2) GLuint vs unsigned int.  On all available platforms, both int and unsigned int are 32 bits (as is GLuint).  There is no 16-bit computer in use capable of modern OpenGL so we needn't consider those.

The ambiguity is with long:

int: always 32 bits
long: either 32 or 64 bits
long long: always 64 bits

I am not arguing against using the GL... types, but I firmly believe the more transparent these issues are the better and more versatile programmers will be.

Finally, a nice optimization you might demo is to turn your vertex buffer into a static member of Sprite, and have each instance track just a position and scale (and other optional attributes) via uniforms.  glBindBuffer() being expensive compared to glUniform...(), this can improve performance when you have a lot of sprites :-)

scotthooper
Автор

Thanks for making these videos! I watched them almost 6 months ago, but I started college and lost interest. Now that I'm taking some CS classes so I can get my Computer Science degree, I thought I would start these tutorials again. I know the basics and most intermediate things in C++ from teaching myself, so this should be fun.

kensclark
Автор

You make complex stuff easy to understand. Thank you so much.

allisonmerry
Автор

I get "Unknown override specifier" error when I want to create an object of Sprite in Maingame.h, any help ?

gigamak
Автор

+Jawad Nasar Shah That is a null pointer exception, meaning you tried to access a pointer that was = nullptr. When the exception comes up, it the break button and it should take you to the offending line of code!

makinggameswithben
Автор

Really awesome videos, dude. Enjoying every minute!

levinthal
Автор

This is brilliant. Minor nitpick - didn't we have member initialisation since C++11? `class Widget {int foo{42}; };` initialises the member data without repeating the typing in the constructor.

RoamingAdhocrat
Автор

+Hikari Shousai Hmm that's a bit strange. Been a while since I looked at the code for this, try moving on to the next tutorials where we learn about shaders and see if the problem gets fixed.

makinggameswithben
Автор

First let me say that I really appreciate this tutorial series, one of the best around !
You explain everything in plain english, which makes it very easy to understand.
Now to the questions : 1. why do we only use GLuint and not the rest of the GL type defs (GLint, GLfloat..) ? is there some advantage or disadvantage in performance ?
(2. after all, doesnt the compiler check for one more line of code and expression ?)

cheers

calecacciatore
Автор

1. I still don't understand the glBindBuffer, what exactly are we binding and to what? I understand that glGenBuffer, generates a buffer and assigns it vboid, but what exactly does glBindBuffer do?
2. What happens if we don't unbind it?
3. Also glBufferData is used to upload the vertex data right? We are uploading this vertex data to the vbo in the cpu? If to cpu when are we sending it the gpu, as you mentioned in your drawing?
4. Why do we need glDisableVertexAttribArray?
5. Also if you gave the 1.0f for both x and y, in the _sprite.init(), instead of -1.0's, then wouldn't the square go outside the screen, because you said x + width and y + height? 
Happy new year and Thanks in advance...

nsv.
Автор

Is there an easier way of doing this other than hard coding verticies one by one in the future? Also, I don't fully understand the verticies and how they are working here.

decilgang
Автор

why did you set the x, y, w, and h in the init function instead of just putting it in the constructor?

cog
Автор

Hi Ben, first off all thanks for the tutorials, they are great and are helping me a lot.

But I'm having a little problem with the glGenBuffers(1, &_vboId); from the Sprite class, it gives me a Segmentation fail and I haven't been able to solve it, do you have any idea?

andresscarpone