2

Currently I'm working using OpenGLES 2.0 in Android, and I'm struggling to develop a flexible programming architecture to dynamically handle different shaders and different objects.

As an example, I have two classes: SimpleShader and ImageShader.

To draw a SimpleShader, it needs to have an array of vertices specified:

SimpleShader s = new SimpleShader;
s.draw(final FloatBuffer pFloatBuffer);

Similarly, for an ImageShader, it needs both a set of points and a texture to draw:

ImageShader i = new ImageShader;
i.draw(final FloatBuffer pFloatBuffer, final Texture pTexture);

My question is this; SimpleShader and ImageShader are both types of Shader. I'm looking for some kind of design pattern which would allow me to use an abstract draw() method. Can anyone make any recommendations? I'd imagine it has something to do with objects to draw inheriting a Shader-specific drawing interface?

tshepang
  • 12,111
  • 21
  • 91
  • 136
Mapsy
  • 4,192
  • 1
  • 37
  • 43
  • 1
    Make an `interface Shader` with the `draw()` method. Then `implements` it into all the Shader classes. – vallentin Jan 19 '14 at 23:42
  • Would this mean that the interface would contain multiple different kinds of draw() methods and the class would give a concrete implementation of the method of interest and leave other methods blank? – Mapsy Jan 19 '14 at 23:44
  • jedwards, I just deleted your answer because I'm a complete noob. I'm really sorry. About your code; this would have meant that SimpleShader would have been unnecessarily passed an empty Texture. I want to try and avoid this kind of design. – Mapsy Jan 19 '14 at 23:54
  • I've found an answer on StackOverflow which relates to this question; you are not supposed to treat shaders in such a generic fashion. There has to be an element of shader-specific operation within OpenGL ES. http://stackoverflow.com/questions/17446793/engine-rendering-pipeline-making-shaders-generic – Mapsy Jan 20 '14 at 05:01

3 Answers3

1

First you will need a wrapper class as parameter. Such as:

public class ShaderParameter{
    public FloatBuffer PFloatBuffer;
}

Make it constructor injected if PFloatBuffer is required (mandatory).

Then again you can create a base class for drawing. Such as:

public abstract class Shader{
    public void Draw(ShaderParameter param);
}

Then you can inherit those class to meet your needs:

public class ImageShaderParameter extends ShaderParameter{
    public Texture PTexture;
}

public class ImageShaderWrapper extends Shader{
    public override void Draw(ShaderParameter param){
        ImageShaderParameter parameter = (ImageShaderParameter)param;
        ImageShader i = new ImageShader;
        i.draw(parameter.PFloatBuffer, parameter.PTexture);
    }
}

This can be enhanced more using generics to avoid parameter casting and strategy pattern. However that explanation is too long and suitable for other question.

(The code may be based by C# since my experience is more with C#)

Fendy
  • 4,565
  • 1
  • 20
  • 25
  • Even if it goes in a similar direction, I would stick to the strategy pattern. Use composition. – Rafa Jan 22 '14 at 08:41
  • Personally I haven't said that strategy pattern approach cannot be used with my design. Using generics and derived `ShaderParameter` as overloading and composition can replace my design well. Since I do not know java generic very well, I just giving the basic illustration. – Fendy Jan 22 '14 at 08:52
0

You could try the Strategy pattern.

It focusses on different behaviour between entities. So you could have an abstract class "Shader", which has a specific draw-behaviour.

3sdmx
  • 356
  • 2
  • 6
  • I've looked at this pattern; it seems to assume that all implementations require the same parameter structure. In this instance, the parameters change between Shader classes, so I don't think this pattern would work! Thanks for your help. – Mapsy Jan 20 '14 at 00:14
0

You could set the Texture as a property on the Shader and have a single-argument draw if that is applicable to your abstraction. Or you could generalize the Texture argument so that it becomes sensible to always pass it as a second argument to any Shader. I.e. maybe it should extend a Paint interface like in java.awt. Or it could be part of a DrawContext with more properties, so you would always pass a DrawContext as a second argument to the draw method. Thirdly, you could look at java.awt.Graphics2D, which is like a DrawContext that also encapsulates the drawing target/surface. It would be the only argument to your draw method.

Erwin Bolwidt
  • 30,799
  • 15
  • 56
  • 79
  • Just to clarify, the Shader object works highly independent of the specific texture it deals with. This is done so that we only need one ImageShader for many Texture objects. In this regard, the Shader cannot contain it's own Texture; it needs to be passed one. I like the idea that it could be passed an object that defines the drawing operation, this is something I've thought about, however I was hoping there'd be a solution where this could be avoided so that each Shader is only ever provided with the data it needs. – Mapsy Jan 20 '14 at 01:08
  • If you're specifically looking for a design pattern, and the concept of a Texture itself cannot be meaningfully made more generic, perhaps the Command pattern makes sense. It depends on *why* you want to make the `draw` command generic. If it is, so that you can compose chains of draw commands, perhaps manipulate the order in which they are executed, this may be a good option. I think we need more information about your intended use of the draw method to determine which approach is appropriate for your situation. – Erwin Bolwidt Jan 20 '14 at 15:28