Incorrect Clipping and 3D Projection when using SlimDX
I am working on a simple project whereby I wish to display a 3D object within a WinForms application using SlimDX. I have created a small project to do this, but I am encountering a problem where the object I have rendered is being clipped between 0.0f and -1.0f.
I have looked at a friend's code for a similar project (they do not have this problem) and cannot work out why it is happening. I've had to restrict the size of my object to the range of -0.1f -> 0.1f in order for me to be able to see it. Extending my far plane does nothing. The project my friend is using can load objects over 500 times the size of mine, with no clipping issues.
Does anyone have any suggestions? (Please see below for the screen shots and code)
Form Code
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
}
}
Vertex Shader Code
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;
}
Pixel Shader Code
struct PixelShaderInput
{
float4 Color : TEXCOORD0;
};
float4 DefaultPixelShader ( PixelShaderInput input ) : COLOR0
{
return input.Color;
}
I found the solution. Basically, when using the Vertex and Pixel Shaders separately in DirectX (or in this case, SlimDX) and using the SetVertexShaderConstant function to pass the matrices to the vertex shader, those matrices are transposed and stored as row-major matrices, instead of column-major.
There are two ways of resolving this.
- Pre-transpose all matrices before passing them to the vertex shader using the SetVertexShaderConstant function on the graphics device.
- Make use of the DirectX Effect framework, which handles this pre-transposition itself, making it easier to work with the matrices.
Indication of this difference that lead to the resolution can be found here.
精彩评论