• Advertisement
Sign in to follow this  

Meshes and the Programmable Pipeline

This topic is 4229 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi guys. I'm using C# and MDX. In my last project I used the fixed-function pipeline and the Mesh helper class, using Mesh.DrawSubset() to draw the Mesh. Now, however, I am using shaders and Mesh.DrawSubset() doesn't seem to work. I'm thinking this is because Mesh expects values like World and View in order to draw, when, of course, I'm not setting these, as I am doing all the transformations in a shader. I tried to draw the Vertex Buffer using this code:
obj.Shader.Effect.BeginPass(i);
_device.BeginScene();
_device.VertexFormat =
  obj.Mesh.VertexFormat;
_device.SetStreamSource(
  0, obj.Mesh.VertexBuffer,
  0, CustomVertex.PositionNormal.StrideSize);
_device.DrawPrimitives(
  PrimitiveType.TriangleStrip, 0, obj.Mesh.NumberFaces);
_device.EndScene();
obj.Shader.Effect.EndPass();

But I don't really know what I'm doing, I've always just used Mesh.DrawSubset(). The above code "sort of" works. I can see random vertices floating around and can tell that they are colored appropriately by the shader. However, the Mesh is a sphere and that is not at all what is drawn. What is drawn is literally a couple of specks in random places. So what can I do to draw a Mesh, or do I have to use an explicit vertex buffer and stuff?

Share this post


Link to post
Share on other sites
Advertisement
Just a guess since I don't deal with managed directx, but I'm fairly sure that meshes use index buffers as well, so you should set the index stream and use DrawIndexedPrimative.

Share this post


Link to post
Share on other sites
Hmm... Thanks. I tried this now:


obj.Shader.Effect.BeginPass(i);
_device.BeginScene();
//obj.Mesh.DrawSubset(0);
_device.VertexFormat =
obj.Mesh.VertexFormat;
_device.SetStreamSource(
0, obj.Mesh.VertexBuffer,
0, CustomVertex.PositionNormal.StrideSize);
_device.Indices = obj.Mesh.IndexBuffer;
_device.DrawIndexedPrimitives(
PrimitiveType.TriangleList, 0, 0, obj.Mesh.NumberVertices, 0, obj.Mesh.NumberFaces);
_device.EndScene();
obj.Shader.Effect.EndPass();



Now I see nothing but black (the clear color).

Share this post


Link to post
Share on other sites
Do you have lighting enabled? Try disabling that or changing the clear color to something else. You might need a material on your mesh to see something.

Share this post


Link to post
Share on other sites
Tried setting the Lighting to false, didn't work. Tried changing the clear color to white, didn't work (well, the screen was clearing white alright, but the mesh wasn't drawn). And I didn't find a Material property (or anything similar) in Mesh.

Share this post


Link to post
Share on other sites
Well, I got it working in unmanaged, maybe you can compare and see what your doing wrong.


D3DDevice->SetRenderState(D3DRS_LIGHTING, false);

D3DDevice->BeginScene();

IDirect3DIndexBuffer9 *pIB;
pMesh->GetIndexBuffer(&pIB);
IDirect3DVertexBuffer9 *pVB;
pMesh->GetVertexBuffer(&pVB);

D3DDevice->SetIndices(pIB);
D3DDevice->SetStreamSource(0, pVB, 0, pMesh->GetNumBytesPerVertex());
D3DDevice->SetFVF(pMesh->GetFVF());
D3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, pMesh->GetNumVertices(), 0, pMesh->GetNumFaces());
//pMesh->DrawSubset(0);
D3DDevice->EndScene();






Does it work if your not using your shader?

Share this post


Link to post
Share on other sites
The implementation of Mesh.DrawSubset() is similar to this:

1) Find the attributerange with the attribute ID you specify
2) Set the index buffer to the mesh index buffer
3) Set stream source 0 to the mesh vertex buffer
4) Set vertex declaration to mesh vertex declaration
5) Call DrawIndexedPrimitive() with the attributerange from 1

That's really all there's to it. It doesn't change any transformation state (the matrices you speak of), nor does it change any rendering state (materials, textures, etc).

If you see nothing when you draw the mesh now, but it worked before, then your shaders are wrong, or the data for the shaders is wrong. There's some small chance that things like the depth range or blending is wrong, too, but that should be easily checked.

To debug the "nothing draws" problems, I've taken to running the program in Pix, and capturing a frame. Then I find the DrawIndexedPrimitive() call, click the "render" tab, then click the device pointer in the command stream. This lets me inspect the entire device state, which will typically lead me to what the reason is it doesn't draw. It'll also allow me to inspect the vertex and index buffers, as well as the shaders -- pretty slick!

Share this post


Link to post
Share on other sites
Thanks. StormArmageddon, that's basically the code I tried using and nothing was drawing. HPplus, I tried drawing without the effect, with the same result. Also, the shader worked perfectly in FX Composer. Could it be the problem that the camera is inside the mesh? Basically, the camera is inside this spherical "arena" and both are centered at the origin.

Edit: I just ran the program through PIX, like you advised, and although this was my first time using the program, I was able to find that it is called DrawPrimitives 0 times. Interesting.

Share this post


Link to post
Share on other sites
Ok, I tried playing around with drawing the buffer directly and it "sort of" worked. I didn't look like I wanted it to, but with certain types of Primitives I tried it actually looked like a circle sometimes. Of course, sometimes when I played around with it, it made my system crash and corrupted the user config files, so I'm reluctant to do that again. I also started clearing the ZBuffer in addition to the Target, which enabled me to see the lines better. However, Mesh.DrawSubset(0) still doesn't display anything no matter how much I move/rotate the camera.

Edit, Mesh.DrawSubset(0) works. I am only able to see it when I move the camera way outward and then look back towards the center. However, it displays as some sort of cone-like figure instead of a circle. Hmmm...

[Edited by - yaroslavd on July 28, 2006 2:12:05 PM]

Share this post


Link to post
Share on other sites
If it shows up when you move the camera out a bunch it may have something to do with your projection and view matrixes. This is how the DX SDK samples set them up:


D3DXVECTOR3 eye(0.0, 0.0, -2*g_fObjectRadius);
D3DXVECTOR3 at(0.0, 0.0, 0.0);
D3DXVECTOR3 up(0.0, 1.0, 0.0);

D3DXMATRIX view;
D3DXMatrixLookAtLH(&view, &eye, &at, &up);

D3DXMATRIX project;
D3DXMatrixPerspectiveFovLH(&project, D3DX_PI/4, (float)x/y, g_fObjectRadius/64.0f, g_fObjectRadius*200.0f);



Try adjusting g_fObjectRadius and see if you can get it to show up.

Share this post


Link to post
Share on other sites
Well, I'm almost positive my World and View are correct (I printed them out and both are initially just the identity). Perspective, I'm not so sure about. I used this to initialize it:

_camera.Projection = Matrix.PerspectiveLH(this.Width, this.Height, 0.1f, 100000.0f);



"This" pointer is the Form. Is this right? There is also a PerspectiveFovLH() method that I saw. Which one should I use? Also, I was kind of assuming all the matrices work because I have a skybox that draws perfectly whenever I move (I have that code commented out for now to see the sphere better, but I just tried it again and it works and moves around perfectly).

Share this post


Link to post
Share on other sites
Well, I'm stumped. Basically, the only way I can see anything is if I move away from the center. Eventually I see the object. What I see is basically a cone sliced into two along the y-axis, with its tip at the very center of the screen (I also get the impression that it has infinite height). I can roll and pitch the camera and it reacts properly. However, it still looks like the geometrical object I described above, not like a sphere that I created like so:

_mesh = Mesh.Sphere(renderer.Device, radius, 20, 20);

The effect is sorta working properly, meaning it changes the vertex color properly. I don't think that the shader is the problem becomes it is simply a vertex shader that multiplies the vertex by WVP and changes the color (the latter part works perfectly). The shader works perfectly in FX Composer.

Oh, and I took a screenshot of what it looks like:


[Edited by - yaroslavd on July 29, 2006 3:48:27 AM]

Share this post


Link to post
Share on other sites
Where is the code that is uploading the world/view/projection matrices to the shader? The image looks like the transformation matrices contain infinite values.

Share this post


Link to post
Share on other sites

// Data member
EffectHandle _WVP;
...
// In the constructor
_WVP = this.Effect.GetParameterBySemantic(null, "WorldViewProjection");
...
// In Update()
this.Effect.SetValue(_WVP, entity.World * Camera.View * Camera.Projection);




I also had Update() output WVP every frame to the console and this is what it looks like once I am able to see an image similar to the one above:

Determinant: 3.051735E-09
M11: 0.0001184798
M12: -0.0001272368
M13: -0.02519732
M14: -0.0251973
M21: -2.983021E-06
M22: -1.187924E-05
M23: 0.9979671
M24: 0.9979662
M31: -0.0001018219
M32: -0.0001477046
M33: -0.05855649
M34: -0.05855644
M41: -0.05602973
M42: -0.0812777
M43: -32.32208
M44: -32.22205


Does that look wrong? I am thinking that maybe my View matrix may actually be wrong. Basically, I assign View = UserShip.World because the camera follows the user ship (which I am not rendering). Is this right? It seemed to work perfectly when I was drawing the skybox, but maybe it's just a coincidence.

Edit: Here's the code which controls the User Ship World matrix (and thus the camera's View matrix - again, not sure if I'm supposed to do that, but here it is):


public void MoveForward(float timeMoving)
{
Velocity += Acceleration * timeMoving; // v = v + at

// Move in the direction of the Z-axis
World *= Matrix.Translation(Velocity * timeMoving * World.M31,
Velocity * timeMoving * World.M32,
Velocity * timeMoving * World.M33); // x = x + vt
}

public void RollLeft(float speed, float timeElapsed)
{
RollRight(-speed, timeElapsed);
}

public void RollRight(float speed, float timeElapsed)
{
World *= Matrix.RotationYawPitchRoll(0.0f, 0.0f, RollAmount(speed, timeElapsed));
}

public void PitchUp(float speed, float timeElapsed)
{
World *= Matrix.RotationYawPitchRoll(0.0f, PitchAmount(speed, timeElapsed), 0.0f);
}

public void PitchDown(float speed, float timeElapsed)
{
PitchUp(-speed, timeElapsed);
}



[Edited by - yaroslavd on July 29, 2006 4:47:57 AM]

Share this post


Link to post
Share on other sites
That does sound like the wrong thing. Instead of assigning the ships world matrix as the view matrix, have a look at the mdx equivalent of D3DXMatrixLookAtLH(), which uses the cameras position, target and up vector to generate a view space matrix. The world matrix you are using should only be used to transform vertices from object space to world space.

Share this post


Link to post
Share on other sites
So how would I build that based on the ship's world matrix? I want the camera to be at the ship's position and looking along the ship's Z-vector, with up being the ship's Y-vector (it's a 3d space ship so I can't just use [0,1,0]). I tried this:


View = Matrix.LookAtLH(new Vector3(w.M41, w.M42, w.M43),
new Vector3(w.M31, w.M32, w.M33),
new Vector3(w.M21, w.M22, w.M23));



But that doesn't work properly.

Share this post


Link to post
Share on other sites
set the position to be the ships position. set the target to be the ships position + ships velocity, set the up vector to be the roll of the ship. I dont know how you would extract those (aside from position) from the world matrix, so store the values separately and build the matrices from those them instead.

Share this post


Link to post
Share on other sites
Yeah, I realized that the above LookAt Matrix was wrong and changed it to this:


View = Matrix.LookAtLH(new Vector3(w.M41, w.M42, w.M43),
new Vector3(w.M41 + w.M13, w.M42 + w.M23, w.M43 + w.M33),
new Vector3(w.M12, w.M22, w.M32));




The camera now moves as I'd expect it to, at least in relation to the skybox. It behaves EXACTLY the same as if I just assigned View = World. Grrrrr...

Also, I'm trying to base the camera code off of this: Camera Tutorial.

[Edited by - yaroslavd on July 29, 2006 9:02:48 PM]

Share this post


Link to post
Share on other sites
I think I know what might be wrong with my code. The tutorial I mentioned says that the 4th vector of the matrix is -position.localX, -position.localY, -position.localZ, not just position.X, position.Y, position.Z. So I'm confused about how position is stored in the world matrix and how that translates to camera position. I'm almost positive the rotation of the camera is correct (the skybox which is infinitely far away is yaw/pitch/rolled perfectly), but I can't say the same for the position.

Share this post


Link to post
Share on other sites
Also, I just tried this:


View = Matrix.LookAtLH(new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(0, 1, 0));



just to make sure I had a valid View matrix. And... I don't see any of the Sphere that is supposed to be drawn. I'm completely stumped.

Share this post


Link to post
Share on other sites
Update: I can finally see the sphere. It's in the wrong place, my camera may still be wrong, etc. However, I can at least see it. The fix? I specified cullmode = none in the shader. I guess since I was inside of the sphere, it was culling some of it out. Now to get the view right...

Share this post


Link to post
Share on other sites
try this:

World = Matrix.Identity();

View = Matrix.LookAtLH(new Vector3(0, 0, -10), new Vector3(0, 0, 1), new Vector3(0, 1, 0));

currently you have the camera positioned at the origin looking in the x direction. If the sphere is also centred at the origin you shouldnt see anything if culling is turned on.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement