Trying to render a texture on Windows.Forms.Panels

牧云@^-^@ 提交于 2020-01-05 13:06:14

问题


Using a SlimDX.Windows.RenderForm, I want to dynamically create a texture of a map of the US, copy that texture to 2 other textures, then draw unique radar data onto each texture. Then I will pass those 2 textures to each be drawn over its own Windows.Forms.Panel. This way, the user will be able to, for example, view base reflectivity and base velocity side by side in the 2 panels. Right now, I've successfully generated my map of the US onto a texture. But when I try to render it to the panels, I get black (and btw, my clear color is a soft grey, not black).

EDIT: After some playing around, I have modified my code to take that Texture that the TextureRenderer is using, turn it to a stream, and create a new Texture under the device of the RuntimeRenderer. Now I have a soft grey square with a yellow square inside it (which is compatible with the -0.9, -0.9... 0.9, 0.9 I use for vertices). I know the new Texture is correct, but the square is still yellow.

Here's my code:

using SlimDX;
using SlimDX.D3DCompiler;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using SlimDX.Windows;
using System;
using System.Windows.Forms;
using System.Collections.Generic;

using Device = SlimDX.Direct3D11.Device;
using Buffer = SlimDX.Direct3D11.Buffer;
using Resource = SlimDX.Direct3D11.Resource;
using Format = SlimDX.DXGI.Format;
using MapFlags = SlimDX.Direct3D11.MapFlags;



namespace Radar
{
abstract public class Renderer
{
    protected Device mDevice = null;
    protected SwapChain mSwapChain = null;
    protected RenderTargetView RenderTarget { get; set; }

    public Device Device { get { return mDevice; } protected set { mDevice = value; } }
    public DeviceContext Context { get { return Device.ImmediateContext; } }
    protected SwapChain SwapChain { get { return mSwapChain; } set { mSwapChain = value; } }

    protected int RenderTargetIndex { get; set; }
    protected VertexShader VertexShader { get; set; }
    protected PixelShader PixelShader { get; set; }
    protected Buffer VertexBuffer { get; set; }
    protected Buffer MatrixBuffer { get; set; }
    protected InputLayout Layout { get; set; }
    protected ShaderSignature InputSignature { get; set; }
    protected SamplerState SamplerState { get; set; }



    protected void CreateDevice(IntPtr inHandle)
    {
        SwapChainDescription chainDescription = new SwapChainDescription()
        {
            BufferCount = 2,
            Usage = Usage.RenderTargetOutput,
            OutputHandle = inHandle,
            IsWindowed = true,
            ModeDescription = new ModeDescription(0, 0, new Rational(60, 1), Format.R8G8B8A8_UNorm),
            SampleDescription = new SampleDescription(1, 0),
            Flags = SwapChainFlags.AllowModeSwitch,
            SwapEffect = SwapEffect.Discard
        };

        Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.Debug, chainDescription, out mDevice, out mSwapChain);
    }



    protected void SetupViewport(int inWidth, int inHeight)
    {
        Viewport viewport = new Viewport(0.0f, 0.0f, inWidth, inHeight);
        Context.OutputMerger.SetTargets(RenderTarget);
        Context.Rasterizer.SetViewports(viewport);
    }



    public void Clear()
    {
        Context.ClearRenderTargetView(RenderTarget, new Color4(0.117f, 0.117f, 0.117f));
    }



    public void Present()
    {
        SwapChain.Present(0, PresentFlags.None);
    }



    public virtual void Dispose()
    {
        SamplerState.Dispose();
        VertexBuffer.Dispose();
        Layout.Dispose();
        InputSignature.Dispose();
        VertexShader.Dispose();
        PixelShader.Dispose();
        RenderTarget.Dispose();
        SwapChain.Dispose();
        Device.Dispose();
    }



    public class RenderTargetParameters
    {
        public int Width { get; set; }
        public int Height { get; set; }
        public IntPtr Handle { get; set; }


        public RenderTargetParameters()
        {
            Width = 0;
            Height = 0;
            Handle = new IntPtr(0);
        }
    }



    // the inCount parameter is used in the TextureRenderer, but not in the code I've given
    public abstract void Render(int inCount = -1);
    public abstract void Prepare(string inShaderName = null);

}



// I use this to render the texture. This is working fine
public class TextureRenderer : Renderer
{
    .
    .
    .
}



public class RuntimeRenderer : Renderer
{
    public TextureRenderer TextureRenderer { get; protected set; }



    public RuntimeRenderer(RenderTargetParameters inParms, ref TextureRenderer inTextureRenderer)
    {
        CreateDevice(inParms.Handle);

        TextureRenderer = inTextureRenderer;

        // I save to a file to make sure the texture is what it should be (and it is)
        TextureRenderer.Save();

        using (Resource resource = Resource.FromSwapChain<Texture2D>(SwapChain, 0))
            RenderTarget = new RenderTargetView(Device, resource);

        SetupViewport(inParms.Width, inParms.Height);

        using (var factory = SwapChain.GetParent<Factory>())
            factory.SetWindowAssociation(inParms.Handle, WindowAssociationFlags.IgnoreAltEnter);
    }



    public override void Prepare(string inShaderName)
    {
        DataStream data = new DataStream(12 * 4, true, true);
        data.Write(new Vector3(-0.9f, -0.9f, 0.0f));
        data.Write(new Vector3(-0.9f, 0.9f, 0.0f));
        data.Write(new Vector3(0.9f, -0.9f, 0.0f));
        data.Write(new Vector3(0.9f, 0.9f, 0.0f));
        data.Position = 0;

        DataStream indices = new DataStream(sizeof(int) * 4, true, true);
        indices.Write(0);
        indices.Write(1);
        indices.Write(2);
        indices.Write(3);
        indices.Position = 0;

        VertexBuffer = new Buffer(Device, data, (int)data.Length, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
        Buffer indexBuffer = new Buffer(Device, indices, (int)indices.Length, ResourceUsage.Default, BindFlags.IndexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);

        Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(VertexBuffer, 12, 0));
        Context.InputAssembler.SetIndexBuffer(indexBuffer, Format.R32_UInt, 0);
        Context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleStrip;

        using (ShaderBytecode bytecode = ShaderBytecode.CompileFromFile("ShaderFX.fx", "SimpleVertexShader", "vs_5_0", ShaderFlags.Debug, EffectFlags.None))
        {
            InputSignature = ShaderSignature.GetInputSignature(bytecode);
            VertexShader = new VertexShader(Device, bytecode);
        }

        using (ShaderBytecode bytecode = ShaderBytecode.CompileFromFile("ShaderFX.fx", "SimplePixelShader", "ps_5_0", ShaderFlags.Debug, EffectFlags.None))
            PixelShader = new PixelShader(Device, bytecode);

        InputElement[] inputEl = new InputElement[2];
        inputEl[0].SemanticName = "POSITION";
        inputEl[0].SemanticIndex = 0;
        inputEl[0].Format = Format.R32G32B32_Float;
        inputEl[0].Slot = 0;
        inputEl[0].AlignedByteOffset = 0;
        inputEl[0].Classification = InputClassification.PerVertexData;
        inputEl[0].InstanceDataStepRate = 0;

        inputEl[1].SemanticName = "TEXCOORD";
        inputEl[1].SemanticIndex = 0;
        inputEl[1].Format = Format.R32G32_Float;
        inputEl[1].Slot = 0;
        inputEl[1].AlignedByteOffset = InputElement.AppendAligned;
        inputEl[1].Classification = InputClassification.PerVertexData;
        inputEl[1].InstanceDataStepRate = 0;

        Layout = new InputLayout(Device, InputSignature, inputEl);

        BufferDescription matrixDescription = new BufferDescription()
        {
            Usage = ResourceUsage.Dynamic,
            SizeInBytes = sizeof(float) * 16 * 3,
            BindFlags = BindFlags.ConstantBuffer,
            CpuAccessFlags = CpuAccessFlags.Write,
            OptionFlags = ResourceOptionFlags.None,
            StructureByteStride = 0
        };

        MatrixBuffer = new Buffer(Device, matrixDescription);

        ShaderResourceViewDescription resourceViewDescription = new ShaderResourceViewDescription()
        {
            Format = TextureRenderer.Texture.Description.Format,
            Dimension = ShaderResourceViewDimension.Texture2D,
            MipLevels = TextureRenderer.Texture.Description.MipLevels,
            MostDetailedMip = 0
        };

        ShaderResourceView resourceView = new ShaderResourceView(Device, TextureRenderer.Texture, resourceViewDescription);

        SamplerDescription samplerDescription = new SamplerDescription()
        {
            Filter = Filter.MinMagMipLinear,
            AddressU = TextureAddressMode.Wrap,
            AddressV = TextureAddressMode.Wrap,
            AddressW = TextureAddressMode.Wrap,
            MipLodBias = 0.0f,
            MaximumAnisotropy = 1,
            ComparisonFunction = Comparison.Always,
            BorderColor = new Color4(0, 0, 0, 0),
            MinimumLod = 0,
            MaximumLod = 99999
        };

        SamplerState = SamplerState.FromDescription(Device, samplerDescription);

        Context.InputAssembler.InputLayout = Layout;
        Context.VertexShader.Set(VertexShader);
        Context.PixelShader.Set(PixelShader);
        Context.PixelShader.SetSampler(SamplerState, 0);

        Vector3 position = new Vector3(0.0f, 0.0f, 10.0f);
        Vector3 target = new Vector3(0.0f, 0.0f, 0.0f);
        Matrix view = Matrix.LookAtLH(position, target, Vector3.UnitY);
        Matrix projection = Matrix.PerspectiveFovLH((float)(Math.PI / 4.0), 1.0f, 0.0f, 100.0f);
        Matrix world = Matrix.Identity;

        DataBox dataBox = Context.MapSubresource(MatrixBuffer, MapMode.WriteDiscard, MapFlags.None);
        dataBox.Data.Write(world);
        dataBox.Data.Write(view);
        dataBox.Data.Write(projection);

        Context.UnmapSubresource(MatrixBuffer, 0);

        Context.VertexShader.SetConstantBuffer(MatrixBuffer, 0);
        Context.PixelShader.SetShaderResource(resourceView, 0);
    }



    public override void Render(int inCount = -1)
    {
        Context.DrawIndexed(4, 0, 0);
    }
}
}

And here is my shader file (ShaderFX.fx):

float4 VShader(float4 position : POSITION) : SV_POSITION
{
return position;
}



float4 PShader_Counties(float4 position : SV_POSITION) : SV_Target
{
return float4(0.5f, 0.5f, 0.5f, 1.0f);
}



float4 PShader_States(float4 position : SV_POSITION) : SV_Target
{
return float4(0.883f, 0.883f, 0.883f, 1.0f);
}



struct VertexShaderInput
{
float2 pos : POSITION;
};



struct PixelShaderInput
{
float4 pos : SV_POSITION;
};



PixelShaderInput SimpleVertexShader(VertexShaderInput input)
{
PixelShaderInput vertexShaderOutput;

vertexShaderOutput.pos = float4(input.pos, 0.5f, 1.0f);

return vertexShaderOutput;
}



float4 SimplePixelShader(PixelShaderInput input) : SV_TARGET
{
return float4(1.0f, 1.0f, 0.0f, 1.0f);
}

回答1:


Quite a few things here.

First you create one device per renderer, which is not necessary, you can render everything against the same device.

To create a device:

Device device = new Device(DriverType.Hardware,DeviceCreationFlags.Debug);

Then you can create several swapchains using same device (one for each panel)

SwapChain swapchain = new SwapChain(device.Factory, device, chainDescription);

Second issue, your quad is missing texture coordinates, so you will not be able to display a texture using it. Please note that you of course need only one quad per device, you don't need one per renderer.

DataStream data = new DataStream(20 * 4, true, true);
data.Write(new Vector3(-0.9f, -0.9f, 0.0f));
data.Write(new Vector2(0.0f,1.0f));

data.Write(new Vector3(-0.9f, 0.9f, 0.0f));
data.Write(new Vector2(0.0f,0.0f));

data.Write(new Vector3(0.9f, -0.9f, 0.0f));
data.Write(new Vector2(1.0f,1.0f));

data.Write(new Vector3(0.9f, 0.9f, 0.0f));
data.Write(new Vector2(1.0f,0.0f));
data.Position = 0;

Your input layout already takes Texture Coordinate into account, so you don't need to modify that.

Next you don't use any of the projection matrices in your vertex shader, so unless you plan to, you can just ignore that code.

Finally, using a single device, make sure device context calls are in the render function, for example:

public override void Render(int inCount = -1)
{
    Context.OutputMerger.SetTargets(RenderTarget);

    Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(VertexBuffer, 12, 0));
    Context.InputAssembler.SetIndexBuffer(indexBuffer, Format.R32_UInt, 0);
    Context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleStrip;

    Context.InputAssembler.InputLayout = Layout;
    Context.VertexShader.Set(VertexShader);
    Context.PixelShader.Set(PixelShader);
    Context.PixelShader.SetSampler(SamplerState, 0);
    Context.VertexShader.SetConstantBuffer(MatrixBuffer, 0);
    Context.PixelShader.SetShaderResource(resourceView, 0);

    Context.DrawIndexed(4, 0, 0);
}

Finally you will of course need to pass texture coordinates in your shader in order to sample the texture properly:

struct vsInput
{
float4 position: POSITION;
float2 texCoord: TEXCOORD0;
};

struct psInput
{
    float4 posScreen : SV_POSITION;
    float2 texCoord : TEXCOORD0;
};

Texture2D texture : register(t0);
SamplerState linearSampler : register(s0);

psInput VS(vsInput input)
{
    psInput output;
    output.posScreen = input.position;
    output.texCoord = input.texCoord;
    return output;
}

float4 PS(psInput input): SV_Target
{
    return texture.Sample(linearSampler, input.texCoord);
}


来源:https://stackoverflow.com/questions/24212019/trying-to-render-a-texture-on-windows-forms-panels

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!