Jump to content
  • Advertisement
Sign in to follow this  
SelethD

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

This topic is 1346 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

 

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!