0
\$\begingroup\$

I'd like to draw a sprite bitmap with SlimDX Direct3D10, but I'm stuck at this problem, where I can see my image on screen, but it gets stretched depending on the window size.

The issue looks like this:

Stretched Sprite 1 Stretched Sprite 2

I got the same general idea of drawing sprites in Direct3D9 and Direct2D working fine, because there I simply resize the RenderTarget or reinitialize the Device with a new BackBuffer.

How do I fix this, so that the sprite remains the same dimensions independent from window size?

I'm seeing two potential issues:

  • I don't know if and how I need to set the different Transforms to adjust for window size (I'd rather resize the backbuffer, and only transform sprites for positioning and scaling)
  • I suppose I need to update the backbuffer or do something in the form's resize event.

Here is my rendering code:

using DiscSim;
using SlimDX;
using SlimDX.Direct3D10;
using SlimDX.DXGI;
using System.Windows.Forms;
using System.Drawing;
using Device = SlimDX.Direct3D10.Device;
using Vector2 = SlimDX.Vector2;
using Resource = SlimDX.Direct3D10.Resource;

namespace DiscSimSlimDX
{
    class Camera_DX10 : Camera
    {
        private RenderTargetView renderTarget;
        private Device device;
        private SwapChain swapChain;
        private Sprite sprite;
        private SpriteInstance[] spriteInstances;

        public Camera_DX10(ISimulation simulation, Form form, string spritePath)
            : base(simulation, form, spritePath)
        {
            var swapChainDescription = new SwapChainDescription()
            {
                BufferCount = 1,
                ModeDescription = new ModeDescription(0, 0, new Rational(60, 1), Format.R8G8B8A8_UNorm),
                IsWindowed = true,
                OutputHandle = form.Handle,
                SampleDescription = new SampleDescription(1, 0),
                Flags = SwapChainFlags.AllowModeSwitch,
                SwapEffect = SwapEffect.Discard,
                Usage = Usage.RenderTargetOutput
            };

            Device.CreateWithSwapChain(null,
                DriverType.Hardware,
                DeviceCreationFlags.Debug,
                swapChainDescription,
                out device,
                out swapChain);

            using (var backBuffer = Resource.FromSwapChain<Texture2D>(swapChain, 0))
            {
                renderTarget = new RenderTargetView(device, backBuffer);
                RenderTargetViewDescription r = renderTarget.Description;
            }

            device.OutputMerger.SetTargets(renderTarget);
            device.Rasterizer.SetViewports(new Viewport(0, 0, form.ClientSize.Width, form.ClientSize.Height, 0, 1));

            device.OutputMerger.BlendState = BlendState.FromDescription(device,
                new BlendStateDescription()
                {
                    IsAlphaToCoverageEnabled = true,
                });

            sprite = new Sprite(device, 512 * 512); // How big should the buffer size be?
            sprite.ProjectionTransform = Matrix.Identity; // ?
            sprite.ViewTransform = Matrix.Identity; // ?

            ShaderResourceView texView = ShaderResourceView.FromFile(device, "Resources/World.png");
            var spriteInstance = new SpriteInstance(texView, Vector2.Zero, new Vector2(1, 1));
            spriteInstance.Transform = Matrix.Identity; // ?

            spriteInstances = new SpriteInstance[1] { spriteInstance };

            form.ResizeEnd += (s, e) =>
            {
                // How do I resize the backbuffer or what do I need to do
                // to adjust for the new window size?

            };
        }

        public override void Draw()
        {
            device.ClearRenderTargetView(renderTarget, Color.Black);
            sprite.Begin(SpriteFlags.None);
            sprite.DrawBuffered(spriteInstances);
            sprite.End();
            swapChain.Present(0, PresentFlags.None);
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ Why are you using DirectX 10 instead of DirectX 11? \$\endgroup\$ – Chuck Walbourn Oct 26 '17 at 5:07
  • \$\begingroup\$ Because I'd like to compare different graphic APIs for a project at university. Specifically I'm looking for the best trade-off between performance and ease-of-use when drawing 2D bitmaps. DX11 will probably have the best performance, but it doesn't have an easy to use Sprite class like DX9 or 10, which both seem to require far less code. But maybe in the end, it won't be worth it. \$\endgroup\$ – Xarbrough Oct 26 '17 at 15:19
  • 1
    \$\begingroup\$ Ah, ok. DirectX 11 is basically the same as DirectX 10.x in terms of the graphics API with DirectX 11 having better functionality, better hardware support, and better multi-threading behavior. You are actually evaluating the deprecated D3DX utility library here and not the graphics API which is where sprites were implemented for DX9 and DX10. For DirectX 11, a good replacement for C++ is DirectX Tool Kit. \$\endgroup\$ – Chuck Walbourn Oct 26 '17 at 16:14
  • \$\begingroup\$ Note that if performance is an important criteria for your evaluation, you should not be using C# but C++. C# is often "good enough", but you will have a ton of effects from the .NET codegen esp. floating-point instructions, the P/Invoke overhead, and garbage collection. \$\endgroup\$ – Chuck Walbourn Oct 26 '17 at 16:19
  • \$\begingroup\$ Thank you for the additional info! I do understand the differences better now, but when I started out the goal was to tackle one of the managed wrappers, because that's my main language and I wanted to see how far I would get and which API (or toolkit) was giving good results. My conclusion is: Direct2D and DX9 are easy to set up in SlimDX, and faster than WinForms etc, but slower than DX10 and 11. DX11 is fastest, but almost as cumbersome to setup as in C++, so I'd go for either D2D for quick and dirty or DX11 in C++ for performance. \$\endgroup\$ – Xarbrough Oct 26 '17 at 18:06
1
\$\begingroup\$

After some experimentation I've found this to work:

// Buffer-size = number of sprites in this case. Max 4096
sprite = new Sprite(device, sim.discs.Count) 
{
    ViewTransform = Matrix.Identity,
    ProjectionTransform = Matrix.OrthoOffCenterLH(
        0, form.ClientSize.Width, -form.ClientSize.Height, 0, 0, 1)
};

I honestly don't exactly understand my solution. The sprite is probably the render target and can be transformed to project from model to world space. I'm guessing, that I don't need the view transform in my case, because the camera is supposed to be orthographic.

To scale and move the individual sprite instances I do:

spriteInstance.Transform = Matrix.Scaling(size, size, 1) * Matrix.Translation(d.x, -d.y, 0);

Where size is in pixels on the screen and d.x, d.y are the position in pixel coordinates.

Another thing I don't understand: I had to flip the projection matrix y-axis to make anything appear on screen, but as I understand it, this would be different than how Direct3D internally handles it. It works, but it's weird to have to invert my translation.


Although my particular problem was solved through experimentation, I'd be very happy about responses with a better (or more correct) approach to help me understand.


Additions (after more experimentation):

I've been implementing sprite-drawing in DX11 in the meantime and understood the underlying convention better: DirectX commonly uses a left-handed coordinate system in 3D (with the y-axis pointing up), but I thought of 2D coordinates (as with sprites) going from top to bottom. Everything is easier, when I stick to the 3D convention.

viewProjection =
    Matrix.Scaling(zoom, zoom, 1f) *
    Matrix.Translation(PositionX, -PositionY, 0f) *
    Matrix.OrthoLH(ScreenWidth, ScreenHeight, 0f, 1f);

This way, I don't have to flip any position vectors, I just assume positive y values mean going to the screen top. Additionally, I found that the SlimDX method Matrix.OrthoLH gives me correct results, where OrthoOffCenterLH seems to flip the y-axis, not sure if I wasn't using it correctly or if it might even be a bug.

Everything works now, but I'm a little unsure about what common practice would be. Currently, I'm multiplying scale, translation and aspect ratio (window size) all into the same matrix and assign it to the ViewTransform property. It might be more common to separate those transformations into view, projection and world space.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.