Rendering an object with different shaders to different renderTargets

My objective is relatively simple: I want to use deferred rendering in a 2d scene. Multiple objects will have different textures aimed at different rendertargets, like diffuse, specular and normal maps.

My initial approach was to add multiple bitmaps, each with their respective map, to an object (let’s say a spaceship). Unfortunately, it seems I can render the entire scene only to a texture, so this wouldn’t work. I’d need render multiple times (that’s normal) and add a logic to hide or show different bitmap each time (kinda heavy).

A scene handles layers, but objects don’t, so I can’t but different maps on different layers and simply render a unique layer.

Creating 3 different scenes with three different spaceships, each with a bitmap, seems like a lot of work if I get a lot of spaceships.

I took a look at RenderContext but could not find something that would help me.

Finally, I might have seen some kind of GBuffer in 3D so an approach would be to convert my project to 3D, using planes for 2d objects, but I’d prefer a 2D approach for now.

Any ideas? Did I miss a simple thing that would help me?

Alternatively, I could override the Bitmap’s draw() function on a custom DeferredBitmap object and only render if a certain RenderContext is passed.
Thus, a diffuse texture bitmap would only draw itself if the passed context is the diffuse rendercontext. This way, I could simply have different contexts and create bitmaps expecting certain contexts. This wouldn’t break the scene tree and be simple to implement.

I’ll keep on writing here in case someone else has a similar problem. Ok, so the current approach seems to be working. I’m still not sure if it’s the right one though. I created two objects, a ContextBuffer and a BufferRenderer.

class ContextBuffer
    {
        public var context(default, null):RenderContext;
        public var target(default, null):Texture;
        public var bitmap(default, null):Bitmap;

        public function new(aScene:Scene, aWidth:Int, aHeight:Int):Void
        {
            context = new RenderContext(aScene);
            target = new Texture(aWidth, aHeight, [Target]);
            bitmap = new Bitmap(Tile.fromTexture(target));
        }

        public function render():Void
        {
            context.pushTarget(target);
            context.begin();
            context.clear(0);
            context.drawScene();
            context.popTarget();
            context.end();
        }
    }
class BufferRenderer
{
    public var buffers(default, null):Array<ContextBuffer>;
    public var width(default, null):Int;
    public var height(default, null):Int;
    public function new(aWidth:Int, aHeight:Int):Void
    {
        width = aWidth;
        height = aHeight;
        buffers = [];
    }

    public function createBuffer(aScene:Scene):ContextBuffer
    {
        var buffer:ContextBuffer = new ContextBuffer(aScene, width, height);
        buffers.push(buffer);
        return buffer;
    }

    public function render():Void
    {
        for (buffer in buffers)
        {
            buffer.render();
        }
    }
}

Finally, I extended the Bitmap class in a ContextBitmap that will only render in the correct context. Is this the right approach? I don’t know, but it sure fits the litteral meaning of “context”, so for now it works.

class ContextBitmap extends Bitmap
{
    public var expectedContext:RenderContext;
    public function new(?context:RenderContext, ?tile:h2d.Tile, ?parent:h2d.Object):Void
    {
        expectedContext = context;
        super(tile, parent);
    }

    override private function draw(ctx:RenderContext):Void
    {
        if (ctx == expectedContext)
        {
            super.draw(ctx);
        }
    }
}

Now it’s all a matter of creating a ship, putting each texture in a ContextBitmap of the right context (Normal Texture in NormalContext, Diffuse Texture in DiffuseContext, etc.) and it renders correctly. The mask bitmap without context is a black bitmap of the ship to hide stuff that are under it as it renders before the current pass.

renderer = new BufferRenderer(engine.width, engine.height);
 var bufferScene = new Scene();
var diffuse = renderer.createBuffer(bufferScene);
var normal = renderer.createBuffer(bufferScene);

var ship:Object = new Object(bufferScene);

var shipMask = new Bitmap(shipImage.toTile(), ship);
var shipDiffuseBitmap = new ContextBitmap(diffuse.context, shipImage.toTile(), ship);
var shipNormalBitmap = new ContextBitmap(normal.context, shipNormal.toTile(), ship);

This is all terribly complicated and I feel like there’s a better solution for 2d multi-pass deferred rendering.

The ultimate objective is to have normal maps in a 2d game (which does not need all of this) but also support an insane amount of lights and other effect passes I come up with (I plan on using shaders to show physical damage to ships using some kind of damage heightmap and a damage texture).

1 Like