Jump to content
  • Advertisement
Sign in to follow this  
nagajim

Render to Surface

This topic is 4871 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

Hi Guys, I'm using Managed Code - and I know most of you (perhaps all?) are using unmanaged but I hope you can help me. I have loaded large image from a file onto a surface imagesurface = graphicsdevice.CreateOffscreenPlainSurface(image.fromfile(strfile).width, image.FromFile(strfile).Height, format.X8B8G8R8, pool.Scratch); surfaceloader.FromFile(imagesurface, rectangle.FromLTRB(0, 0, intmaxX, intmaxY), strfile, rectangle.FromLTRB(0, 0, intmaxX, intmaxY), filter.None, color.AliceBlue.ToArgb); But how can I render additional items onto this surface. (basically lines & boxes - data held of file as X-Y pairs of line end points + colour for each line.) Thanks in advance, Jim

Share this post


Link to post
Share on other sites
Advertisement
Rendering to a surface is typically explained as rendering the entire 3D scene to a texture surface, which can be re-used later. I'm doing this using the following code:

Define in OnDeviceReset handler (rts and renderTexture are class vars in my code):

rts = new RenderToSurface(e.Device, renderSurfaceWidth, renderSufaceHeight, glowTextureFormat, true, DepthFormat.D16);
renderTexture = new Texture( e.Device, renderSurfaceWidth, renderSufaceHeight, 1, Usage.RenderTarget, textureFormat, Pool.Default);


Render the scene to the surface, MUST be called OUTSIDE the normal device.BeginScene() and device.EndScene() block, otherwise things will lock up.

// may be defined in OnDeviceReset too, would be better...
Viewport view = new Viewport();
view.Width = renderSurfaceWidth;
view.Height = renderSufaceHeight;
view.MaxZ = 1.0f;

using (Surface renderSurface = renderTexture.GetSurfaceLevel(0))
{
rts.BeginScene( renderSurface, view );
device.Clear( ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.Transparent, 1.0f, 0);

// ADD CODE TO RENDER YOUR SCENE HERE

rts.EndScene(Filter.None);
}


After executing the render code above, your surface should contain a render of your scene. In your case you'll probably have to render a Sprite (a helper quad for easily rendering to screen coordinates) to paint the background texture every time, because the device.Clear call will clear the existing texture surface data.

You might get away with reusing the surface you created in the code you posted above, but the performance will suffer because it is not set up as a render target. Rendering the texture every frame using the sprite might even give a better performance.

I hope this reply was a bit useful :)

Share this post


Link to post
Share on other sites
nagajim,

The solution to your problem lies in "render targets." By default, whenever you call DrawPrimitives, etc...you're drawing the objects to the device's backbuffer. This then gets pushed to the screen whenever you call Present. What you'd like to do is temporarily change where you are rendering to, in other words, you'd like to change your Render Target.

Conveniently, Microsoft has provided just such functionality by using the SetRenderTarget method of the Device class. Use the following pseudo-code to guide you.

// Store off the backbufer surface so you restore it afterwards
Surface BackBuffer = Device.GetBackBuffer();

// Set your surface as the new render target
Device.SetRenderTarget( 0, MySurface );

// Draw your primitives here as usual
// ..
// ..

// Restore the surface
Device.SetRenderTarget( 0, BackBuffer );

When the above pseudo-code is implemented, anything you draw within the "draw" section above will be rendered to your surface - a cube, a mesh, anything....

Enjoy!

Share this post


Link to post
Share on other sites
Thanks Jeromy,

It seems such an elegant solution.

However, I still have a problem. My code to create my surface is
imagesurface = graphicsdevice.CreateOffscreenPlainSurface(image.FromFile(strfile).Width, image.FromFile(strfile).Height, format.X8B8G8R8, pool.Scratch);

and the Scratch pool prevents that surface from being a Render Target. If I use Default or Managed or SystemMemory - my code fails at the above line with D3DERR_INVALIDCALL. Using Scratch I can get my image (or a screen size part of it) displayed.

Initially my images were non power of 2 (ie 3000x900) but I get the same results if I use 512x512 images.

I'm hopeful if I can overcome the "pool" problem then your original solution will just snap into place for me.

Thanks again,
Jim

Share this post


Link to post
Share on other sites
Naga,

This is a common problem as well.

What you're probably looking for is the ability to have a fixed texture, and then render on top of it a primite or set of primitives, etc...But if you think on it a sec, it'll become obvious that after your first render your surface will have been modified with the previous drawprimitves call. So any successive draw calls will just draw over your existing image...bad =(. What you really need is 2 or 3 different surfaces...it works different in managed than unmanaged...essentially you need to do the following:

1. Create scratch surface via texture from file.
2. Create default pool texture or surface with the Render Target flag

Now, each frame you copy the scratch texture into your default pool or managed pool texture, and then set that second texture as the render target. This will then draw over the texture with your primitives, while preserving your original texture loaded from file. Due to DirectX pickyness there might be need for a 3rd texture, but you can cross that bridge when you get to it.

Cheers and good luck!

Share this post


Link to post
Share on other sites
Jeromy,
Again many thanks. This is becoming a little complicated.

What I want to do is load an image from file to a surface (I choose a surface rather than a Texture as my images are large and non powers of 2 - I ran into problems with images > 2048x2048 when using Textures). I then wnat to draw additional (fixed) items onto this surface - such that the composite now becomes my fixed background image. A one time operation at load time.

Then every frame I copy a portion (that which is visible after scrolling) of this surface to my Texture's surface and display using Sprite.Draw2D.

I was hoping I would only need one surface for this background, albeit created in a 2 stage process (using your solution of setting a different Render target).

However, it seems that I can create a surface to hold these large images but can't then easily Render additional items on them.

Should I change my images from file to be multiples of 1024x1024 or 512x512 such that I can load them onto a number of Textures, which will then be easier to Render my static elements to?

Speed at load time is not an issue, although speed thereafter is.

Again many thanks for you time and patience.

Jim

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.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!