devkitPro / citro3d

Homebrew PICA200 GPU wrapper library for Nintendo 3DS
zlib License
244 stars 34 forks source link

Offscreen Rendertargets #40

Closed TurtleP closed 6 years ago

TurtleP commented 6 years ago

Hey I was wondering: is it possible to make offscreen rendertargets?

For example, I want to draw something once to the rendertarget and then later draw it as one big image. My current code doesn't work, and I'm not sure that it's doable right now.

See here for more details.

Also, code:

int Graphics::SetCanvas(lua_State * L)
{
    if (!lua_isnoneornil(L, 1))
    {   
        Canvas * self = (Canvas *)luaobj_checkudata(L, 1, LUAOBJ_TYPE_CANVAS);

        lastCanvas = self;

        C3D_FrameBegin(C3D_FRAME_SYNCDRAW);

        C3D_FrameDrawOn(self->GetTarget()->GetTarget());

        //C3D_SetFrameBuf(&self->GetTarget()->GetTarget()->frameBuf);

        C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, projection_desc, self->GetTarget()->GetProjection());

        self->GetTarget()->Clear(graphicsGetBackgroundColor());
    }
    else
    {
        if (lastCanvas == nullptr)
            return 0;

        C3D_FrameBuf * fb = &lastCanvas->GetTarget()->GetTarget()->frameBuf;
        lastCanvas->SetTexture(fb);

        C3D_FrameSync();
    }

    return 0;
}
fincs commented 6 years ago

You can create a rendertarget that renders to texture using C3D_RenderTargetCreateFromTex. In your rendering section, you first bind to said rendertarget and render whatever you want, then when you bind to other rendertargets you can bind the texture you passed to C3D_RenderTargetCreateFromTex, and use its image in further rendering that may go on screen. Hopefully this is what you wanted. (Also, keep in mind that textures must be a power of two).

TurtleP commented 6 years ago

I think that's what I want. It's just the name that's kind of confusing me and the code as well. Do you have an example of its usage? To me, the way it sounds like it's taking an image and using that as the render target, but I need to make it so I return a C3D_Tex * from it, not a C3D_Rendertarget * since the way it'll be used is rendering an image, not a target.

fincs commented 6 years ago

There is a citro3d test program that used render to texture, however it hasn't been updated to use the new API and it's currently broken (sorry for the inconvenience, we are now working on it). The idea is to create a texture object (C3D_Tex), and later create a rendertarget that references this texture. Something like this basically:

C3D_Tex tex;
C3D_TexInitVRAM(&tex, 256, 256, GPU_RGBA8);
C3D_RenderTarget* texTarget = C3D_RenderTargetCreateFromTex(&tex, GPU_TEXFACE_2D, 0, GPU_RB_DEPTH24_STENCIL8);

//... then in your rendering code
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
C3D_FrameDrawOn(texTarget);
//... perform drawing on texture
C3D_FrameDrawOn(targetTop);
C3D_TexBind(&tex);
// use texture for drawing more stuff, this time on the screen...
C3D_FrameEnd(0);
TurtleP commented 6 years ago

Holy crap that makes a lot more sense now. Thanks :D

fincs commented 6 years ago

( ͡° ͜ʖ ͡~)

TurtleP commented 6 years ago

I think the name for the function may be a bit misleading, imo, since it doesn't sound like you can draw anything or that perhaps it only takes textures or something. Maybe name it C3DRenderToTexture? ¯\_(ツ)/¯ (fixed my shrug)

mtheall commented 6 years ago

You're creating a render target. The name literally means a thing you can draw on. C3D_RenderTargetCreateFromTex means "create a render target from a texture".

Now, when you bind the render target and draw on it, it will actually be rendering to the texture, which can be used later. A name like C3D_RenderToTexture implies a lot of higher-level operations all happening together, so I don't think that's fitting at all.

The best alternative I can think would be C3D_FrameDrawOnTex(C3D_Tex *tex) which might automatically allocate a render target for you but then nobody knows when or where to clean up that render target.

TurtleP commented 6 years ago

One quick thing: it seems what I render is being offset/scaled or something..

screenshot from 2017-09-29 18-16-57

Here's the code:

//graphics.cpp

int Graphics::SetCanvas(lua_State * L)
{
    if (!lua_isnoneornil(L, 1))
    {   
        Canvas * self = (Canvas *)luaobj_checkudata(L, 1, LUAOBJ_TYPE_CANVAS);

        C3D_FrameBegin(C3D_FRAME_SYNCDRAW);

        C3D_FrameDrawOn(self->GetTarget()->GetTarget());

        C3D_FVUnifMtx4x4(GPU_VERTEX_SHADER, projection_desc, self->GetProjection());

        self->GetTarget()->Clear(graphicsGetBackgroundColor());
    }
    else
    {
        C3D_FrameSync();
    }

    return 0;
}

//crendertarget.cpp
CRenderTarget::CRenderTarget(love::Image * texture, int width, int height)
{
    this->target = C3D_RenderTargetCreateFromTex(texture->GetTexture(), GPU_TEXFACE_2D, 0, GPU_RB_DEPTH24_STENCIL8);
    Mtx_OrthoTilt(&projection, 0.0, width, height, 0.0, 0.0, 1.0, true);

    //C3D_RenderTargetSetClear(this->target, C3D_CLEAR_ALL, 0x000000FF, 0);
}

Expected result: screenshot from 2017-09-29 18-19-04

Rendering normally without the canvas/rendertarget yields the "expected" result.

fincs commented 6 years ago

Keep in mind that if you're drawing to texture, you should use the non-tilt versions of the projection matrices. That is, use Mtx_Ortho instead of Mtx_OrthoTilt.

TurtleP commented 6 years ago

Oh, my bad. Thanks again :+1:

mtheall commented 6 years ago

Also remember the difference between your texture width/height/aspect and the screen's width/height/aspect. Also make sure to remember that texture coordinate origin is bottom left. There's a lot of math to keep in sync.

TurtleP commented 6 years ago

Got it. Although, one last thing (sorry for all this hassle), since I have to keep it (the texture passed in) as a power of two, it's stretching the contents, which I'm not sure why it would.

fincs commented 6 years ago

You're probably rendering the Po2 texture with the same dimensions and the screen - that will obviously stretch the image. You can create a texture that is slightly bigger than the screen but still Po2, and use C3D_Viewport to restrict rendering to a (screen-sized) window within the texture; then when you are going to render the texture use the correct texture coordinates to only render this window.

TurtleP commented 6 years ago

Alright so since implementation, I've come across a few other issues. If I make the texture to render to in RAM there's very few missing spots of pixels in the first one, but subsequent ones may not render everything properly (missing pixels). These also seem to not always render in the spot I specifu, although it could be my code doing this I'm still unsure.

Placing the texture inside VRAM causes heavy garbage to be generated (and in some cases, VRAM corruption or something since I see home menu graphics appear).

I'm not sure what I'm doing wrong.. I start the frame, draw on the target, do the Mtx4x4 on the projection and after this is done, call end frame. Any ideas to fix this?

SeleDreams commented 1 month ago

Hi, I was wondering if there were any examples of how the generated texture would be rendered on the screen ?

oreo639 commented 1 month ago

Probably best not to necro 6+ years old gh issues (there is https://devktipro.org for general help), I did update the old c3d test if you want to see that for reference: https://github.com/oreo639/citro3d/blob/8feafecb2339950120c7482bdee3bba9bd65e2d6/test/3ds/source/main.cpp#L571 (it is just the same as drawing any texture)

Maybe worth adding offscreen rendertargets to 3ds-examples?

SeleDreams commented 1 month ago

Sorry, didn't see the date. When I searched on google, the only related thing I found was this discussion

Le ven. 26 juil. 2024 à 04:22, oreo639 @.***> a écrit :

Probably best not to necro 6+ years old gh issues (there is https://devktipro.org for general help), I did update the old c3d test if you want to see that for reference: @.*** https://github.com/oreo639/citro3d/commit/8feafecb2339950120c7482bdee3bba9bd65e2d6

Maybe offscreen rendertargets could be added to 3ds-examples.

— Reply to this email directly, view it on GitHub https://github.com/devkitPro/citro3d/issues/40#issuecomment-2251843395, or unsubscribe https://github.com/notifications/unsubscribe-auth/AD4UF4IKBPJZNYU432NHYADZOGXHVAVCNFSM6AAAAABLPTMMSKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENJRHA2DGMZZGU . You are receiving this because you commented.Message ID: @.***>