Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (513)
Games in Android Showcase (121)
games submitted by our members
Games in WIP (577)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  New to OpenGL, need some guidence on 2D Game / sprites and VBO  (Read 3671 times)
0 Members and 1 Guest are viewing this topic.
Offline GiraffeTap22

Innocent Bystander





« Posted 2012-11-06 09:26:57 »

Hello everyone!

I decided to start a new project and make a game and I want to keep it 2D sprite based only. So I started learning today how to use OpenGL
and was able to get a hang of it very qucikly.

Now I really just have few questions about using VBOs and using them for sprites

1. I know VBO is considered the fastest when it comes to rendering, but is it the best option for a 2D only game?
2. Would I be able to transform sprites when and if I need to (Rotation and scale)?
3. What about transforming sprites based on a world matrix or 2D camera?

And my last question!

I created the below code which uses a VBO to render the sprites I have in an array of sprite objects.
Is this the right way / best way of rendering quads with textures on them using a VBO? Is there a better way?

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
//Temp Class for sprites
   public class Sprite
   {
      public float x;
      public float y;
      public Texture texture;
   }

//Function used to "run" the game
   public void run()
   {

//Create an array of sprites and init them
      Sprite [] sprite = new Sprite[2];
      sprite[0] = new Sprite();
      sprite[0].x = 20;
      sprite[0].y = 20;
      sprite[0].texture = square; //previously loaded texture using the slick texture and texture loader
     
      sprite[1] = new Sprite();
      sprite[1].x = 125;
      sprite[1].y = 125;
      sprite[1].texture = smile; //previously loaded texture using the slick texture and texture loader
     
      //Create the amount of vertexs and sie
      final int amountOfVertex = 4;
      final int vertexSize = 2; / 2 for x and y cord only

//Create the vertex buffer
      FloatBuffer vertexData = BufferUtils.createFloatBuffer(amountOfVertex * vertexSize);

//create and init the texture coord buffer for mapping a texture to a quad
      FloatBuffer texCoordData = BufferUtils.createFloatBuffer(amountOfVertex * vertexSize);
      texCoordData.put(new float[]{
            0.0f, 0.0f,
            1.0f, 0.0f,
            1.0f, 1.0f,
            0.0f, 1.0f});
      texCoordData.flip();
     
      //Set up the vertex buffer
      int vboVertexHandle = glGenBuffers();
      glBindTexture(GL_TEXTURE_2D, square.getTextureID());
      glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
      glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);
      glBindBuffer(GL_ARRAY_BUFFER, 0);
     
//Set up the texture coord buffer
      int texCoordHandle = glGenBuffers();
      glBindTexture(GL_TEXTURE_2D, texCoordHandle);
      glBindBuffer(GL_ARRAY_BUFFER, texCoordHandle);
      glBufferData(GL_ARRAY_BUFFER, texCoordData, GL_STATIC_DRAW);
      glBindBuffer(GL_ARRAY_BUFFER, 0);

     
      //Init OpenGL      
      while(!Display.isCloseRequested())
      {
         //used for user input
         gamepad.pollInput();
         
//Clear the buffer
         glClear(GL_COLOR_BUFFER_BIT);
         
//Run though the sprite list and draw them to the screen
//Is this the part im doing right? Thanks :D
         for(int i = 0; i < 2; i++)
         {
         
//Create the verteex buffer data based on the sprite to draw
            vertexData.put(new float[]{
                  sprite[i].x, sprite[i].y,
                  sprite[i].x + sprite[i].texture.getImageWidth(), sprite[i].y,
                  sprite[i].x + sprite[i].texture.getImageWidth(), sprite[i].y + sprite[i].texture.getImageHeight(),
                  sprite[i].x, sprite[i].y + sprite[i].texture.getImageHeight()});
            vertexData.flip();
           
//bind the texture to use and handle the rest of the drawing
            glBindTexture(GL_TEXTURE_2D, sprite[i].texture.getTextureID());
            glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
            glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);
            glBindBuffer(GL_ARRAY_BUFFER, 0);
           
            glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
            glVertexPointer(vertexSize, GL_FLOAT, 0, 0L);
           
            glBindBuffer(GL_ARRAY_BUFFER, texCoordHandle);
            glTexCoordPointer(vertexSize, GL_FLOAT, 0, 0L);
                     
            glEnableClientState(GL_VERTEX_ARRAY);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            glDrawArrays(GL_QUADS, 0, amountOfVertex);
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
            glDisableClientState(GL_VERTEX_ARRAY);
         }
         
      //Update and sync it to 60 FPS
         Display.update();
         Display.sync(60);
      }


Any code examples would be greatly appreciated!
Offline sproingie

JGO Kernel


Medals: 202



« Reply #1 - Posted 2012-11-06 15:49:46 »

I would recommend using LibGDX.  The SpriteBatch class does all the VBO assembly and packing for you, and like the name implies, batches them for bulk uploads to the GPU. 

Even if you don't use LibGDX, I'd at least take a look at the code for SpriteBatch to see how it works.
Offline davedes
« Reply #2 - Posted 2012-11-06 17:16:37 »

1. It's a good option. So is vertex arrays. Or geometry shaders, if supported.
2. Yes. Generally scaling would be done on CPU by simply specifying different vertex positions. Rotation could also be done on CPU, by transforming each vertex before placing it in your array. Alternatively, you could send the model transform as a uniform matrix to the shader, which allows you to do the transformation on the GPU. Maybe a system comprised of both would be ideal.
3. Easy as pie if you're using a shader.

LibGDX's sprite batch actually uses simple vertex arrays. When it comes to streaming data every frame, they are almost as fast as VBOs, or on some drivers even faster. There are also some implications on Android.

Your code looks a little strange. Why are you calling glBindTexture on a buffer ID? Creating a new float array every frame seems like a waste. And you're using VBOs for such a small amount of data that it won't be very effective. Further, your system isn't very flexible; what if you wanted to add many more textures?

You should also think about texture atlases; i.e. using a single texture comprised of many sprites, in order to reduces texture binds and batch renders.

I wrote a "bare bones" intro to LWJGL sprite batching that I'd recommend taking a look at:
http://www.java-gaming.org/topics/bare-bones-intro-to-textures-programmable-pipeline/27708/view.html

It introduces you to textures, shaders, a simple orthographic projection matrix, vertex arrays, and a simple SpriteBatch implementation. Once you are comfortable with those ideas, you could try implementing VBOs to see if it's any more performant.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline GiraffeTap222

Senior Newbie





« Reply #3 - Posted 2012-11-07 04:04:14 »

Hey its GiraffeTap22 I forgot my pass and the email I used... anyway...

Thank you both very much for the replies! But if I can I would like to make my game from scratch.

Davedes I looked at the link to your classes. It gave me a few ideas, but I have a few questions about the whole thing

Could you explain the shaders program to me? I am completely confused about them, because when I hear the word shader I think about 3D models and lighting. Something not used in a 2D game.

Also, this maybe stupid of me asking, but what is the difference between drawRegion and draw?

Is drawRegion just drawing sprites that fall in a certain area?

Also I went back and changed my code up a bit. Its seems I can render 10000 sprites in about 180 - 215 milliseconds according to a simple execution run time check. I get the execution time by rendering all the of the sprites once and then closing down the program.

Is there anything else I could do?

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
public class Sprite
   {
      public float x;
      public float y;
      public Texture texture;
   }
   
   public GameWindow()
   {
         
      //init the game window and OpenGl
      initGameWindow();
      initOpenGL();
     
      //Load some textures
      square = loadTexture("square.png");
      smile = loadTexture("smile.png");
         
      gamepad = new GamepadsController();
      //lastFrame = getTime();
   }
   
   public void run()
   {
      //Create an array of sprite objects
      Sprite [] sprite = new Sprite[10000];  
     
      //Create a new random gen
      Random gen = new Random();      
     
      //create sprites and place them randomly based on the screen size
      for(int i = 0; i < sprite.length; i++)
      {
         sprite[i] = new Sprite();
         sprite[i].x = gen.nextInt(SCREENWIDTH) + 1;
         sprite[i].y = gen.nextInt(SCREENHEIGHT) + 1;
         
         //Set the texture
         if(i % 2 == 0)
            sprite[i].texture = square;
         else
            sprite[i].texture = smile;
      }
   
      //set a place holder for the current texture to be rendered
      currentTexture = sprite[0].texture;
     
      //Set up the vertex buffer
      final int amountOfVertex = 4;
      final int vertexSize = 2;

      FloatBuffer vertexData = BufferUtils.createFloatBuffer(amountOfVertex * vertexSize);

      FloatBuffer texCoordData = BufferUtils.createFloatBuffer(amountOfVertex * vertexSize);
      texCoordData.put(new float[]{
            0.0f, 0.0f,
            1.0f, 0.0f,
            1.0f, 1.0f,
            0.0f, 1.0f});
      texCoordData.flip();
     
      int vboVertexHandle = glGenBuffers();
      glBindTexture(GL_TEXTURE_2D, currentTexture.getTextureID());
      glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
      glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);
      glBindBuffer(GL_ARRAY_BUFFER, 0);
     
      int texCoordHandle = glGenBuffers();
      glBindBuffer(GL_ARRAY_BUFFER, texCoordHandle);
      glBufferData(GL_ARRAY_BUFFER, texCoordData, GL_STATIC_DRAW);
      glBindBuffer(GL_ARRAY_BUFFER, 0);
     
      float [] quadPosition = new float[amountOfVertex * vertexSize];
     
      for(int i = 0; i < (amountOfVertex * vertexSize); i++)
         quadPosition[i] = 0.0f;
     
      //Init OpenGL      
      while(!Display.isCloseRequested())
      {
         //poll data
         gamepad.pollInput();
         
         //Clear
         glClear(GL_COLOR_BUFFER_BIT);
         
         //Draw all the sprites
         for(int i = 0; i < sprite.length; i++)
         {
           
            //set the texture for the quad and bind it
            currentTexture = sprite[i].texture;
            currentTexture.bind();
           
            //Test movement
            //sprite[i].x += 1;
           
            //clear the vertex data
            vertexData.clear();
           
            //Set up the position of the quad
            quadPosition[0] = sprite[i].x;
            quadPosition[1] = sprite[i].y;
            quadPosition[2] = sprite[i].x + sprite[i].texture.getImageWidth();
            quadPosition[3] = sprite[i].y;
            quadPosition[4] = sprite[i].x + sprite[i].texture.getImageWidth();
            quadPosition[5] = sprite[i].y + sprite[i].texture.getImageHeight();
            quadPosition[6] = sprite[i].x;
            quadPosition[7] = sprite[i].y + sprite[i].texture.getImageHeight();
           
            vertexData.put(quadPosition);
            vertexData.flip();

            //Bind and draw the Quad
            glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
            glBufferData(GL_ARRAY_BUFFER, vertexData, GL_STATIC_DRAW);
            glBindBuffer(GL_ARRAY_BUFFER, 0);
               
         
            glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
            glVertexPointer(vertexSize, GL_FLOAT, 0, 0L);
           
            glBindBuffer(GL_ARRAY_BUFFER, texCoordHandle);
            glTexCoordPointer(vertexSize, GL_FLOAT, 0, 0L);
                     
            glEnableClientState(GL_VERTEX_ARRAY);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            glDrawArrays(GL_QUADS, 0, amountOfVertex);
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
            glDisableClientState(GL_VERTEX_ARRAY);
         }
         
         Display.update();
         Display.sync(60);
         
      }
     
      Display.destroy();

   }


Thanks again!


The specs on the computer I'm working with

Specs [Dell Laptop] -
OS : Win 7 Home Premium Service Pack 1
Processor : Intel Core Duo t9550 2.66 GHz
Ram : 4 Gb DDR3
Video Card : ATI mobility Radeon HD 4670
Offline StumpyStrust
« Reply #4 - Posted 2012-11-07 04:27:57 »

Haha I just posted a tutorial on how to make a basic spritebatcher...follow davedes as his is more robust but if you want to stay away from shaders look at what I just posted.

drawRegion I think is for drawing only a region of the texture...basically texture atlases and stuff.

Idk what your system specs are but you should be at way higher then 10k sprites in 180-215 milliseconds.

I recommend getting a batcher working using glBegin/glEnd first so you understand what type of abstraction you want to use. Then try out vertex arrays/VBO.

Offline GiraffeTap222

Senior Newbie





« Reply #5 - Posted 2012-11-07 07:37:38 »

Haha I just posted a tutorial on how to make a basic spritebatcher...follow davedes as his is more robust but if you want to stay away from shaders look at what I just posted.

drawRegion I think is for drawing only a region of the texture...basically texture atlases and stuff.

Idk what your system specs are but you should be at way higher then 10k sprites in 180-215 milliseconds.

I recommend getting a batcher working using glBegin/glEnd first so you understand what type of abstraction you want to use. Then try out vertex arrays/VBO.


I should have probably posted my system specs! I would like to use VBOs and the above code is my attempt at them,
but are saying that I should have gotten a way less execution time?

Specs [Dell Laptop] -
OS : Win 7 Home Premium Service Pack 1
Processor : Intel Core Duo t9550 2.66 GHz
Ram : 4 Gb DDR3
Video Card : ATI mobility Radeon HD 4670

I will look into your example as well!
Offline davedes
« Reply #6 - Posted 2012-11-07 07:57:24 »

Shaders can be used for 2D or 3D purposes -- OpenGL doesn't know what kind of game you're making.

drawRegion is used for sprite sheets or "texture atlases" -- for example, instead of loading multiple PNG files into different textures, you create a single texture which contains all of your sprites. That way, you don't need to bind a new texture for each sprite, and you can also batch all of your sprites into the same render call.

You didn't really change or fix your example... It's still rendering one sprite at a time -- the whole point of a sprite batcher is to render many sprites in one render call.

I'd strongly recommend trying to learn the basics, like how to load and draw a texture without using SlickUtil, before tackling something like VBOs. Check out Stumpy's SpriteBatcher guide, and read through the lwjgl-basics library I've posted to try and get a better grasp on what's going on.

Like I said, you don't really need VBOs for a simple sprite renderer... Just stick with vertex arrays.

Offline GiraffeTap222

Senior Newbie





« Reply #7 - Posted 2012-11-07 08:37:48 »

I guess I really didn't understand the concept of a sprite batcher then.

Correct me if I'm wrong. Ok so when we make a sprite batcher, we are really just calculating the vertex, texture, and color data for an X amount of quads (sprites) and then
placing them in giant arrays.

Then when we reach our X amount of quads to handle at one time we render them using a single bind call

So basically this  :

1  
2  
3  
4  
5  
6  
myBatcher.Begin();

myBatcher.draw(sprite1, 50, 50);
myBatcher.draw(sprite2, 12, 9);

myBatcher.End();


Would start up the batcher Then add sprite 1 to the batcher and plan to draw it at x cord 50 and y cord 50.
Then add sprite 2 to the batcher and set up its position. Finally the batcher's end function is called which
actually renders everything and does any clean up




Offline davedes
« Reply #8 - Posted 2012-11-07 08:54:28 »

Yep, that's the basic idea. Calling drawSprite puts that sprite's data into the array, and then increases the index pointer for the next call. Your array (or ByteBuffer) is a fixed size (generally something really big), and you render it all at once.

You only "flush" or "render" the batch when:
- You reach the end of your array (index pointer > max)
- You need to change the texture; i.e. render the batch with the old texture, then bind the new one
- The user explicitly requested you to "flush" or render the batch, i.e. if they wanted to change the blend function.

Offline GiraffeTap222

Senior Newbie





« Reply #9 - Posted 2012-11-07 09:10:23 »

Awesome! One part does part concern me a little, you said I would call my render
function if I changed textures, but wouldn't that have the potential to slow me down?

What I mean is that if sprite 1 is texture A and sprite 2 is texture B then code below

1  
2  
3  
4  
5  
6  
myBatcher.Begin();

myBatcher.draw(sprite1, 50, 50);
myBatcher.draw(sprite2, 12, 9);

myBatcher.End();


is really like doing

1  
2  
3  
4  
5  
6  
7  
myBatcher.Begin();
myBatcher.draw(sprite1, 50, 50);
myBatcher.End();

myBatcher.Begin();
myBatcher.draw(sprite2, 50, 50);
myBatcher.End();


Or am I completely wrong?
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline StumpyStrust
« Reply #10 - Posted 2012-11-07 09:37:48 »

@Phased he may be referring to an earlier post that was long.

@OP: not quite. It can depend on your implementation on how you handle thing but in general it would be more of.

1  
2  
3  
4  
5  
6  
myBatcher.Begin();
myBatcher.draw(sprite1, 50, 50);
myBatcher.End();

myBatcher.draw(sprite2, 50, 50);
myBatcher.End();


The glBegin is normally not expensive as it only sets things up. It is the filling of they arrays/buffers that will slow you down the most. Then if you are using Texture Atlases, flushing to the gpu.

But yes the concept is like that in the respect that once you bind a new texture, everything currently in the batch needs to be sent to the gpu before you can start using the new texture otherwise your sprites will use the wrong image.

Offline masteryoom

JGO Coder


Medals: 5
Projects: 2


If you look closely, you might see it turning...


« Reply #11 - Posted 2012-11-07 09:53:09 »

@Phased he may be referring to an earlier post that was long.
Yes, I was.  Grin

Smiley
Offline davedes
« Reply #12 - Posted 2012-11-07 17:40:19 »

Awesome! One part does part concern me a little, you said I would call my render
function if I changed textures, but wouldn't that have the potential to slow me down?

What I mean is that if sprite 1 is texture A and sprite 2 is texture B then code below
Yes -- this is because (given the way it's currently set up) you can only have one texture bound at a time. That's why I suggested texture atlases to reduce texture binds and increase batch count. Every 2D game should use texture atlases to improve performance, where applicable.

Offline StumpyStrust
« Reply #13 - Posted 2012-11-07 17:54:22 »

Every 2D game should use texture atlases to improve performance, where applicable.

Unless you have card that supports texture arrays.  Grin

Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

theagentd (19 views)
2014-10-25 15:46:29

Longarmx (52 views)
2014-10-17 03:59:02

Norakomi (45 views)
2014-10-16 15:22:06

Norakomi (34 views)
2014-10-16 15:20:20

lcass (39 views)
2014-10-15 16:18:58

TehJavaDev (68 views)
2014-10-14 00:39:48

TehJavaDev (68 views)
2014-10-14 00:35:47

TehJavaDev (60 views)
2014-10-14 00:32:37

BurntPizza (74 views)
2014-10-11 23:24:42

BurntPizza (45 views)
2014-10-11 23:10:45
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!