c# - 使用 SlimDX 时不正确的裁剪和 3D 投影

标签 c# 3d matrix slimdx

我正在做一个简单的项目,我希望使用 SlimDX 在 WinForms 应用程序中显示 3D 对象。我创建了一个小项目来执行此操作,但我遇到了一个问题,即我渲染的对象被剪裁在 0.0f 和 -1.0f 之间。

我看过一个 friend 的类似项目的代码(他们没有这个问题),但无法弄清楚为什么会这样。我不得不将我的对象的大小限制在 -0.1f -> 0.1f 的范围内,以便我能够看到它。扩展我的远平面没有任何作用。我 friend 正在使用的项目可以加载超过我的 500 倍大小的对象,并且没有裁剪问题。

有人有什么建议吗? (请参阅下面的屏幕截图和代码)

Screen Shots

表单代码

namespace TestOfTheTest
{
    using System.Drawing;
    using System.Windows.Forms;
    using SlimDX;
    using SlimDX.Direct3D9;
    using MathHelper = Microsoft.Xna.Framework.MathHelper;

    public struct VertexPositionColor
    {
        private static VertexDeclaration sDeclaration;
        public static VertexElement[] Elements =
        {
            new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0),
            new VertexElement(0, sizeof(float) * 3, DeclarationType.Float4, DeclarationMethod.Default, DeclarationUsage.Color, 0),
            VertexElement.VertexDeclarationEnd
        };

        public Vector3 Position;
        public Color4 Color;

        public VertexPositionColor(Vector3 position, Color4 color)
        {
            this.Position = position;
            this.Color = color;
        }

        public static int DeclarationSize
        {
            get { return (sizeof(float) * 3) + (sizeof(float) * 4); }
        }

        public static VertexDeclaration GetDeclaration(Device device)
        {
            if (sDeclaration == null)
            {
                sDeclaration = new VertexDeclaration(device, Elements);
            }

            return sDeclaration;
        }
    }

    public partial class Form1 : Form
    {
        private Device mDevice;

        private VertexPositionColor[] mVertices;
        private VertexBuffer mVertexBuffer;

        private VertexShader mVertexShader;
        private PixelShader mPixelShader;

        private Point? mLastPosition = null;
        private float mAngle = 0.0f;

        public Form1()
        {
            InitializeComponent();

            this.Load += Form1_Load;
            this.RenderSurface.MouseDown += RenderSurface_MouseDown;
            this.RenderSurface.MouseMove += RenderSurface_MouseMove;
            this.RenderSurface.MouseUp += RenderSurface_MouseUp;
        }

        #region UI Event Handlers

        private void Form1_Load(object sender, System.EventArgs e)
        {
            var parameters = new PresentParameters()
            {
                BackBufferWidth = this.RenderSurface.Width,
                BackBufferHeight = this.RenderSurface.Height,
                Windowed = true,
                DeviceWindowHandle = this.RenderSurface.Handle
            };

            mDevice = new Device(new Direct3D(), 0, DeviceType.Hardware, this.RenderSurface.Handle, CreateFlags.HardwareVertexProcessing, parameters);

            // Create the vertices
            mVertices = new VertexPositionColor[3];

            mVertices[0].Position = new Vector3(-0.1f, -0.1f, -1.0f);
            mVertices[0].Color = new Color4(1.0f, 1.0f, 0.0f, 0.0f);
            mVertices[1].Position = new Vector3(0.0f, 0.1f, -1.0f);
            mVertices[1].Color = new Color4(1.0f, 0.0f, 1.0f, 0.0f);
            mVertices[2].Position = new Vector3(0.1f, -0.1f, -1.0f);
            mVertices[2].Color = new Color4(1.0f, 0.0f, 0.0f, 1.0f);

            // Fill the vertex buffer
            mVertexBuffer = new VertexBuffer(mDevice, VertexPositionColor.DeclarationSize, Usage.WriteOnly, VertexFormat.Position, Pool.Default);
            mVertexBuffer.Lock(0, VertexPositionColor.DeclarationSize * mVertices.Length, LockFlags.None).WriteRange(mVertices);
            mVertexBuffer.Unlock();

            // Load the shaders
            var vsByteCode = ShaderBytecode.CompileFromFile(@"\Shaders\DefaultShader.vs.hlsl", "DefaultVertexShader", "vs_2_0", ShaderFlags.None);
            var psByteCode = ShaderBytecode.CompileFromFile(@"\Shaders\DefaultShader.ps.hlsl", "DefaultPixelShader", "ps_2_0", ShaderFlags.None);

            mVertexShader = new VertexShader(mDevice, vsByteCode);
            mPixelShader = new PixelShader(mDevice, psByteCode);

            // Setup render states
            mDevice.SetRenderState(RenderState.CullMode, Cull.None);
        }

        private void RenderSurface_MouseDown(object sender, MouseEventArgs e)
        {
            mLastPosition = e.Location;
        }

        private void RenderSurface_MouseMove(object sender, MouseEventArgs e)
        {
            if (mLastPosition == null)
            {
                return;
            }

            var position = e.Location;
            var lastPosition = mLastPosition.Value;

            mAngle += ((position.X - lastPosition.X) / 20.0f);

            mLastPosition = position;
        }

        private void RenderSurface_MouseUp(object sender, MouseEventArgs e)
        {
            mLastPosition = null;
        }

        #endregion

        #region Rendering

        public void MainLoop()
        {
            var device = mDevice;

            // Calculate matrices
            Matrix projection = Matrix.PerspectiveFovRH(MathHelper.PiOver4, (float)this.RenderSurface.Width / (float)this.RenderSurface.Height, 1.0f, 1000.0f);
            Matrix view = Matrix.LookAtRH(Vector3.UnitZ, Vector3.Zero, Vector3.UnitY) * Matrix.RotationY(mAngle);
            Matrix viewProjection = view * projection;

            // Initialize the graphics device
            device.VertexShader = mVertexShader;
            device.PixelShader = mPixelShader;

            device.SetVertexShaderConstant(0, viewProjection);

            // Render the scene
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, unchecked((int)0x00000000), 1.0f, 0);
            device.BeginScene();

            device.VertexDeclaration = VertexPositionColor.GetDeclaration(device);
            device.SetStreamSource(0, mVertexBuffer, 0, VertexPositionColor.DeclarationSize);

            device.DrawPrimitives(PrimitiveType.TriangleList, 0, mVertices.Length);

            device.EndScene();
            device.Present();
        }

        #endregion
    }
}

顶点着色器代码

float4x4 mWorldViewProjection;

struct VertexShaderInput
{
    float4 Position : POSITION;
    float4 Color : COLOR;
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float4 Color : TEXCOORD0;
};

VertexShaderOutput DefaultVertexShader ( VertexShaderInput input )
{
    VertexShaderOutput output = ( VertexShaderOutput ) 0;

    // Transform coordinates
    output.Position = mul(input.Position, mWorldViewProjection);

    // Copy other values
    output.Color = input.Color;

    return output;
}

像素着色器代码

struct PixelShaderInput
{
    float4 Color : TEXCOORD0;
};

float4 DefaultPixelShader ( PixelShaderInput input ) : COLOR0
{
    return input.Color;
}

最佳答案

我找到了解决方案。基本上,当在 DirectX(或在本例中为 SlimDX)中分别使用顶点着色器和像素着色器并使用 SetVertexShaderConstant 函数将矩阵传递给顶点着色器时,这些矩阵是 transposed并存储为行优先矩阵,而不是列优先矩阵。

有两种方法可以解决这个问题。

  1. 使用图形设备上的 SetVertexShaderConstant 函数将所有矩阵传递到顶点着色器之前预转置它们。
  2. 利用 DirectX Effect 框架,它可以自行处理这种预转换,从而更轻松地处理矩阵。

可以找到导致解决的这种差异的指示 here .

关于c# - 使用 SlimDX 时不正确的裁剪和 3D 投影,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7739442/

相关文章:

c# - 如何使用 Linq 同时对项目列表进行分组和求和?

python - Pandas:如何在二维以上构建数据?

java - 如何在客户端*无需*安装 Java3D 等插件的情况下在网页上编写 Java 3D 元素

wpf - 如何将许多图像拼接在一起以提供像 Photosynth 一样的 3D 沉浸式体验?

c# - 接口(interface)与 SVG 曲线的区分联合

c# - 将新项目添加到数据网格的 ItemsSource 而不引用 Collection

c# - 创建一个 RegEx 来验证用户名

python - 矩阵计算产生的 numpy 数组中的舍入值

matlab - 矩阵的多个常数并将它们转换为matlab中的 block 对角矩阵

python,测试矩阵中的所有值是否小于 numpy.finfo(float).eps