【问题标题】:Trying to render a texture on Windows.Forms.Panels尝试在 Windows.Forms.Panels 上渲染纹理
【发布时间】:2014-06-13 18:45:56
【问题描述】:

使用 SlimDX.Windows.RenderForm,我想动态创建美国地图的纹理,将该纹理复制到其他 2 个纹理,然后在每个纹理上绘制独特的雷达数据。然后我将传递这 2 个纹理,每个纹理都在其自己的 Windows.Forms.Panel 上绘制。这样,用户将能够例如在 2 个面板中并排查看基本反射率和基本速度。现在,我已经成功地将我的美国地图生成到纹理上。但是当我尝试将它渲染到面板时,我得到了黑色(顺便说一句,我的清晰颜色是柔和的灰色,而不是黑色)。

编辑:经过一番尝试,我修改了我的代码以获取 TextureRenderer 正在使用的纹理,将其转换为流,并在 RuntimeRenderer 的设备下创建一个新的纹理。现在我有一个柔和的灰色正方形,里面有一个黄色正方形(与我用于顶点的 -0.9、-0.9... 0.9、0.9 兼容)。我知道新的纹理是正确的,但正方形仍然是黄色的。

这是我的代码:

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);
    }
}
}

这是我的着色器文件 (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);
}

【问题讨论】:

    标签: winforms directx-11 slimdx


    【解决方案1】:

    这里有很多东西。

    首先,您为每个渲染器创建一个设备,这不是必需的,您可以在同一设备上渲染所有内容。

    创建设备:

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

    然后您可以使用相同的设备创建多个交换链(每个面板一个)

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

    第二个问题,您的四边形缺少纹理坐标,因此您将无法使用它显示纹理。请注意,每个设备当然只需要一个quad,每个渲染器不需要一个quad。

    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;
    

    您的输入布局已将纹理坐标考虑在内,因此您无需对其进行修改。

    接下来,您不会在顶点着色器中使用任何投影矩阵,因此除非您打算这样做,否则您可以忽略该代码。

    最后,使用单个设备,确保设备上下文调用在渲染函数中,例如:

    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);
    }
    

    最后,您当然需要在着色器中传递纹理坐标才能正确采样纹理:

    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);
    }
    

    【讨论】:

    • 非常感谢您的意见。我可以告诉我的菜鸟正在展示。它仍然不能正常工作。我想我没有正确地将我的纹理传递给 HLSL 代码。我想多玩一会儿,然后再回复你。
    • PS,我确实打算使用矩阵。我必须能够放大和缩小地图上的特定区域。所以,没有缩放或旋转,但有很多平移。
    • 好的,我知道了。基本上你说的一切都是正确的,除了这一行: Context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(VertexBuffer, 12, 0));你改变了每个顶点的大小,记得吗?所以 VertexBufferBinding 构造函数中的第二个参数应该是 20。除了那个小问题,你做得很好,先生!非常感谢你!
    • 我还有一个问题。你已经很棒了,所以不要觉得有义务,但如果你有兴趣:stackoverflow.com/questions/24269813/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-27
    • 2012-11-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多