Well, I just uploaded a version with some corrections. It's far from finished, but for now it will gain me some time ;) I implemented the 'bmAddition' blend mode and scaling in both X and Y directions.
As I promised, I'll give some more detailed answers now:
mrToad wrote:I was reading up here:
https://thebookofshaders.com/01/
Which was nice up to a point where it gets into pattern and color math, which is fascinating but not what I need right now. But it's already helping with the whole understanding of a GPU, uniforms, and verts. From what I learned in a short time it seems a shader is a program that operates on every pixel. Still have a lot to learn. I also don't know what the shader files contain exactly, or why they are needed to be separate files, or how to make them. :D
Yeah, The Book of Shaders is a great resource. Have it handy, you may need it in the future ;)
The shader files contain code. If you open them, you'll see that it looks a lot like C, so if you are proficient with it, you'll be coding your own shaders in no time. They don't necessarily need to be separate files, you could embed them in a string inside your program, for the card expects them to be simply plain text. Look at the file 'glshader.bi' to see the implementation, and how this is done.
mrToad wrote:And it appears (understandably for a demo purpose) you are using a separate texture unit for each image. Besides that, this code already comes close to what I am doing with the oldschool pipeline. Add the ability to read from packed sprites, a little vertical/horizontal stretching, bolt in those blends you worked up, basic shapes and lines, and I'd be all set, lol. But it is my desire to understand it as fully as possible. I'm not sure why you call it "horrible and rushed", but being a noob in this area I guess I wouldn't know!
In all honesty, the implementation kind of 'cheats'. Look at the main loop, the following lines:
Code: Select all
'' blit the background
glBlit( screenWidth / 2, screenHeight / 2, texture( 0 ), , alphaShader.ID )
t = timer()
'' blit the sprite
glBlit( mx, my, texture( 1 ), texture( 0 ), addShader.ID, 1.0, ang, abs( sin( radians( scale ) ) ) )
'glBlit( screenWidth / 2, screenHeight / 2, texture( 1 ), texture( 0 ), addShader.ID, 1.0 )
t = timer() - t
There's some interesting things happening here. Put the background blitting inside the timer() calls and see what happens. What!? The demo is actually slower!
What is happening here? What it happens is that the second blitting (the one that's measured) is operating with all the data already in the card, since the first call uploaded it. See why I told you that modern cards are tailored for bulk rendering? You can even store all the needed vertices into one vertex array, and
draw them all at once with a single call. Neat, no?
You can also see something interesting if you comment the blitting of the background. If you do, you'll see that the sprite actually blits a part of the 'background' with it. This happens because
we're sampling the texture that contains the background, not the frame buffer itself (the texture of the background is not altered by the blitting process). To really perform the blits the way they're supposed to work, we'll have to render all our stuff to a texture, bind this texture to a Framebuffer Object (FBO) and then render the Framebuffer Object itself. This is explained in the comments in the code (both in the shaders and on the client).
The reason for having to jump through all these hoops lies in massive parallelization of the GPUs. Nowadays, they have
thousands of cores, and each one of them is capable of processing a pixel asynchronously with the others. So, to be able to get away with all this, the buffer to which one renders needs to be immutable. If this doesn't make sense to you, don't worry for now. Just explaining why I'm doing things the way I do here =D
Another interesting thing: you seem
way too concerned with binding textures. Don't. Back when the GPUs were crap, this was really expensive, but this is not the case anymore. Perhaps you didn't noticed, but
the blitter is actually binding and unbinding the texture each call (the call that draws the sprite even binds two textures!), and I coded this
intentionally to show you that it's really of no concern nowadays. Nonetheless, the less binding the better, but
the way you structure your data and upload it to the card is far, far more important than doing a simple texture bind.
There's more than one way to skin this particular cat, and some are better than others. Generally speaking, the more efficient the method, the less flexible it is. Remember, GPUs are tailored to crunch 3D (and they are darn good at it), and while 2D isn't certainly alien, it does pose some interesting challenges to overcome.