How 'in terms of structure' do I use my shader?

Started by
2 comments, last by SelethD 9 years, 5 months ago

Sorry if I am unsure of the proper terms to use, this is all fairly new to me.

I am working on a game, that uses 8bit indexed graphics (for palette shifting/swapping effects).

After about a month of searching tutorials, forums, etc... I am at the point, where I am doing this.....

Each 'character' has a reference to a sprite (object containing a Texture2D) which holds my character image (using the red value of rgb to be an index to the color palette per pixel)

And each 'character' has a local Texture2D for holding that 'character's unique color palette (since different players might color their character's skin, hair, eyes , etc... a different color)

So, each character has to pass its 'image' and 'palette' to the shader.... this is how I am doing it.

Every time I draw a 'character' I call this member function


         public void RenderAt(float posx, float posy, DuShader shader)
        {
            GL.ActiveTexture(TextureUnit.Texture0);
            mCurrentFrame.Bind();
            shader.SetVariable("texture", (int)0);

            GL.ActiveTexture(TextureUnit.Texture1);
            mPalette.Bind();
            shader.SetVariable("colorTable", (int)1);
            GL.ActiveTexture(TextureUnit.Texture0);

            shader.Begin();
            mCurrentFrame.RenderNoBindAt(posx, posy);
            shader.End();

        }

My issue with this is that I am having to call shader.Begin, and shader.End per draw. I am no expert at openGL, but it just screams at me as 'bad code'

My original thought , was to have the shader.Begin located at the beginning of the draw loop, then have all my Render calls happen, then at the end call shader.End

This however, did not work, for some reason, I cannot execute the code....

GL.ActiveTexture(TextureUnit.Texture0);
mCurrentFrame.Bind();
shader.SetVariable("texture", (int)0);

from between the shader.Begin and shader.End calls, it only works when the code is executed 'before' the shader.Begin

I have no idea why this is the case. My understanding of shaders and howto use them is from examining about 30+ articles and tutorials over a period of about 4 weeks :P

So... finally my question... what is the 'best' meaning most correct or efficient way to have multiple draw calls to a shader, where the two textures (image and palette) are never the same, and not have to begin and end my shader for each one.

Here is my shader.begin and shader.end code...


        public void Begin()
        {
            GL.UseProgram(mProgram);
        }
        public void End()
        {
            GL.UseProgram(0);
        }

And the actual GLSL fragment shader...


uniform sampler2D texture;
uniform sampler2D colorTable;

void main()
{
        vec2 pos = gl_TexCoord[0].xy;
        vec4 color = texture2D(texture, pos);
		vec2 palPos = vec2(color.r,0);
        vec4 newColor = texture2D(colorTable, palPos);
		newColor.a = color.a;
        gl_FragColor = newColor;      
}

The output i get is correct, meaning it 'works' Ijust need to know if im using it correctly or a better way of doing this.

Thanks in advance, I know its a long winded question, and I tend to ramble :P

Advertisement
First off, what is the purpose of Shader.End()? Each new Shader.Begin() will override the previous one. There's little reason to unbind a shader.

So... finally my question... what is the 'best' meaning most correct or efficient way to have multiple draw calls to a shader, where the two textures (image and palette) are never the same, and not have to begin and end my shader for each one.


With simple GL... there isn't. Every time you swap out the shader, or the textures, or the vertex buffers, or other resources, you have to submit a different draw call.

The simple "fix" here is to use batching. If you have 100 objects using the same shader and two different textures then group the objects by texture and draw them all together. You'd still have a separate draw call for each texture, but the draw calls would scale by number of textures, not by number of objects using those textures.

Building off of batching you want to further minimize draw calls (your mCurrentFrame.RenderNoBindAt). You want to submit as many objects as possible with each draw call. This can be done with instancing or by a streaming vertex buffer (or some other far more advanced techniques).

You can minimize the overhead of multiple textures by using a texture array. In such a setup (if both textures are the same size) you can set them as differnet layers in an array. Then you need only bind that single array and tell each object which index into the array to use when sampling the texture in your shader (so an extra vertex attribute). If you have many textures, this might work better for you as it allows more batching at the cost of "fatter" vertex attribute specifications.

In general, just google "opengl draw call batching" and you'll find a billion articles on the topic.

Sean Middleditch – Game Systems Engineer – Join my team!

You only need to call the shader.setVariable just before you bind the shader, then you can do all your calls (binding different textures to your texture units, etc) but can leave those setvariable calls out. So long as you are only using one shader for all your draw calls, this will work just fine. Then you can have a single shader.begin and shader.end surrounding all your texture binds and render calls.

Thanks for the replies, I wouldn't even begin to know how to batch my sprites, as each of the 100 sprites would contain different textures. No duplicating textures.

I see what you mean now about not needing to call 'end' on the shader.

I think I understand about the 'setvariable' once, ,then just bind different textures. I will give this a try XD

Thanks again.

This topic is closed to new replies.

Advertisement