Why does this code work? (Drawing in screen space)

Started by
6 comments, last by dietrich 8 years, 10 months ago

Hey all, I just wanted to run this code by because I don't fully understand it. I wrote this a while ago and never touched this part of my code till now. Here it is.


float aspectRatio = float(softObjPtr->clientRect.left) / float(softObjPtr->clientRect.bottom);
float planeXOrig = -1.0f, planeYOrig = -1.0f;
float planeWidth = 2.0f;
float planeHeight = 2.0f;
 

softObjPtr->pD3D10VertexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&v);
 
v[0] = DX10VERTEX(D3DXVECTOR3(planeXOrig, planeYOrig, 0), D3DXVECTOR4(1, 1, 1, 1), D3DXVECTOR2(0.0f, 1.0f));
v[1] = DX10VERTEX(D3DXVECTOR3(planeXOrig + planeWidth, planeYOrig, 0), D3DXVECTOR4(1, 1, 1, 1), D3DXVECTOR2(1.0f, 1.0f));
v[2] = DX10VERTEX(D3DXVECTOR3(planeXOrig + planeWidth, planeYOrig + planeHeight, 0), D3DXVECTOR4(1, 1, 1, 1), D3DXVECTOR2(1.0f, 0.0f));
v[3] = DX10VERTEX(D3DXVECTOR3(planeXOrig, planeYOrig + planeHeight, 0), D3DXVECTOR4(1, 1, 1, 1), D3DXVECTOR2(0.0f, 0.0f));
 
softObjPtr->pD3D10VertexBuffer->Unmap();

Why does my the code work flawlessly if I'm positioning my quad at -1, -1 with a width of 2? I guess I would understand this if I had a camera and it looked directly at the center then -1 to 1 would cover the entire region correclty, however I don't have a camera, or more correctly, I'm not multiplying this by any projection matrix. It goes straight to v. shader which doesn't modify it at all and gets passed into the pixel shader. Also I read that I have to account for the pixel center/cell center and subtract 0.5f from everything, but my textured quad is displayed correctly as it is? Why?

You didn't come into this world. You came out of it, like a wave from the ocean. You are not a stranger here. -Alan Watts

Advertisement

The output coordinates of your vertex shader are in what they call clip space, any coordinates outside of the (-1, -1, 0), (1, 1, 1) range after perspective division (dividing the xyz output of the vertex shader by w) will get clipped by the pipeline.

After that a so-called viewport transform gets applied mapping your xy coordinates to (0, 0), (viewport_width, viewport_height), with y getting flipped.

In your case you are already outputting vertices in normalized device coordinates, so your plane shows up correctly after the viewport transform.

I gets all your texture budgets!

The output coordinates of your vertex shader are in what they call clip space, any coordinates outside of the (-1, -1, 0), (1, 1, 1) range after perspective division (dividing the xyz output of the vertex shader by w) will get clipped by the pipeline.

After that a so-called viewport transform gets applied mapping your xy coordinates to (0, 0), (viewport_width, viewport_height), with y getting flipped.

In your case you are already outputting vertices in normalized device coordinates, so your plane shows up correctly after the viewport transform.

Thanks, that was a good explanation. I want to make sure I got it right. So the vertex shader transformes the camera space into clip space under the hood without me specifying it to do so? And then this clip space gets transformed into the viewport space correct? And I'm supplying the coordinates already in clip space. But I'm still not sure why I'm getting a correct rectangular view that's not stretched if I'm specifying the a width and height of 2 making it a square? I understand if the clip space was from 0 to 1, in which case 1 would just be 100% coverage, but 0 to 2?

Edit: sorry another question, is it possible to go from world space directly into clip space? Can I just apply a clip space transform to the object in world space to get it there?

You didn't come into this world. You came out of it, like a wave from the ocean. You are not a stranger here. -Alan Watts


Also I read that I have to account for the pixel center/cell center and subtract 0.5f from everything, but my textured quad is displayed correctly as it is? Why?

This was changed in DX10. Texels are now pixel centered, so you don't have to make the adjustment manually.


Also I read that I have to account for the pixel center/cell center and subtract 0.5f from everything, but my textured quad is displayed correctly as it is? Why?

This was changed in DX10. Texels are now pixel centered, so you don't have to make the adjustment manually.

I was actually just going to follow up on that, thanks for clearing that up.

You didn't come into this world. You came out of it, like a wave from the ocean. You are not a stranger here. -Alan Watts


So the vertex shader transformes the camera space into clip space under the hood without me specifying it to do so? And then this clip space gets transformed into the viewport space correct? And I'm supplying the coordinates already in clip space. But I'm still not sure why I'm getting a correct rectangular view that's not stretched if I'm specifying the a width and height of 2 making it a square? I understand if the clip space was from 0 to 1, in which case 1 would just be 100% coverage, but 0 to 2?

In this case there's no notion of camera space at all: you supply the clip space coordinates to the vertex shader, and the pipeline after the vertex shader expects such coordinates, thus no transformation is needed. The viewport transform is then applied, mapping this coordinates into the render target space, that is correct.
Clip space coords range from -1 to 1 on the X and Y axes, and your quad spans the exact same range. As the clip space is mapped to the viewport, so is your quad, effectively becoming a rectangle covering the entire viewport.

Edit: sorry another question, is it possible to go from world space directly into clip space? Can I just apply a clip space transform to the object in world space to get it there?

That's exactly what view and projection transforms do: the view matrix transforms the scene so that the camera is positioned at the origin, then the projection transform maps the camera view volume to the canonical view volume (-1, -1, 0), (1, 1, 1), producing clip space coordinates. You can also multiply these two matrices together, which would give you a single transform, that maps vertices from world space directly into clip space.


So the vertex shader transformes the camera space into clip space under the hood without me specifying it to do so? And then this clip space gets transformed into the viewport space correct? And I'm supplying the coordinates already in clip space. But I'm still not sure why I'm getting a correct rectangular view that's not stretched if I'm specifying the a width and height of 2 making it a square? I understand if the clip space was from 0 to 1, in which case 1 would just be 100% coverage, but 0 to 2?

In this case there's no notion of camera space at all: you supply the clip space coordinates to the vertex shader, and the pipeline after the vertex shader expects such coordinates, thus no transformation is needed. The viewport transform is then applied, mapping this coordinates into the render target space, that is correct.
Clip space coords range from -1 to 1 on the X and Y axes, and your quad spans the exact same range. As the clip space is mapped to the viewport, so is your quad, effectively becoming a rectangle covering the entire viewport.

Edit: sorry another question, is it possible to go from world space directly into clip space? Can I just apply a clip space transform to the object in world space to get it there?

That's exactly what view and projection transforms do: the view matrix transforms the scene so that the camera is positioned at the origin, then the projection transform maps the camera view volume to the canonical view volume (-1, -1, 0), (1, 1, 1), producing clip space coordinates. You can also multiply these two matrices together, which would give you a single transform, that maps vertices from world space directly into clip space.

thank you, that made it much clearer. I also read up on canonical view volumes to get on more solid ground. What I'm still a _little_ hazy about is that you said the projection transform produces clip space. I thought that there has to be a clip matrix to transform from projection to clip space? And I was wondering where that happens, because I don't remember setting a clip matrix anywhere, does DX do it under the hood? Or is it some covert function like SetViewPort or something?

Also I just worked out a little math yesterday, and I came up with this matrix to get from world to clip directly. Just as a side note, I'm working in 2D so Z is always 1. If I have a vector in world space with x, y coords, then in clip space x' = (x/screen_width) * 2 and y' = (y/screen_height)*2 that would make the vector lie in 0 to 2 space, then I subtract 1 from each to put it into canonical space. So the final matrix comes out to

2/scr_width 0 -1

0 2/scn_height -1

0 0 1

will this work ok?

edit: A: no it won't work! the model is not is screen_space Q: what if I make sure that the model never exceeds screen space dimensions in the world, as in the world coordinates will always be more then 0 and less then screen_height, screen_widht? I want to transform a 2D sprite in a 2D software rasterizer, so I really don't need the projection, cameras space etc? Or would it still be better to use them?

You didn't come into this world. You came out of it, like a wave from the ocean. You are not a stranger here. -Alan Watts


What I'm still a _little_ hazy about is that you said the projection transform produces clip space. I thought that there has to be a clip matrix to transform from projection to clip space? And I was wondering where that happens, because I don't remember setting a clip matrix anywhere, does DX do it under the hood? Or is it some covert function like SetViewPort or something?

I believe that the perspective matrix in DirectX is already defined so that any point, that falls into the view frustum, is projected into a point in the range of ( -1, -1, 0 ), ( 1, 1, 1 ), so no additional transform is necessary to map the canonical view volume into clip space, as they are pretty much the same.

This need not be true for any perspective matrix: for instance, the most simple example of such a matrix can look something like this:

1 0 0 0

0 1 0 0

0 0 1 0

0 0 1 0

Multiplying a point P( x, y, z, 1 ) by this matrix yields P'( x, y, z, z ), and after homogenization (division by the w-component) is applied, this becomes P''( x/z, y/z, 1, 1 ). P'' is the projection of point P onto the plane z=1, with perspective foreshortening applied (note that x/z and y/z get smaller as the distance from the viewer increases).

Although the basic idea behind the perspective matrix in DirectX is the same, its layout is a bit more complicated. It scales the point's x and y coordinates to the [-1, 1] range. The z-coordinate is also preserved (unlike the example above) and mapped into [0, 1] range. You can read about DirectX (and OpenGL) projection matrices in grater detail for example in this article (section on Perspective projection).


Also I just worked out a little math yesterday, and I came up with this matrix to get from world to clip directly. Just as a side note, I'm working in 2D so Z is always 1. If I have a vector in world space with x, y coords, then in clip space x' = (x/screen_width) * 2 and y' = (y/screen_height)*2 that would make the vector lie in 0 to 2 space, then I subtract 1 from each to put it into canonical space. So the final matrix comes out to
2/scr_width 0 -1
0 2/scn_height -1
0 0 1

will this work ok?

Actually, it depends. How do you define your world space, i.e. what units do you use and which direction are the x and y axes pointing? The decisions are more or less arbitrary and will depend mostly on your personal taste, but sticking to them consistently matters a lot. The matrix you've constructed should work, if your world units are pixels (sprite positions and scr_width, scr_height are measured in pixels), and you have the X-axis pointing to the right and Y-axis pointing up. If you prefer working in screen space, with the Y-axes pointing down, then your matrix will actually translate your entire screen out of the canonical view space, so nothing will be visible. To fix that you would need to translate +1, not -1 on the y axis, and also mirror the y-coordinates:

2/scr_width 0 -1
0 -2/scn_height 1
0 0 1


edit: A: no it won't work! the model is not is screen_space Q: what if I make sure that the model never exceeds screen space dimensions in the world, as in the world coordinates will always be more then 0 and less then screen_height, screen_widht? I want to transform a 2D sprite in a 2D software rasterizer, so I really don't need the projection, cameras space etc? Or would it still be better to use them?

Not sure if I understand you correctly. You don't have to limit you model coordinates to screen_width, screen_height, unless it is supposed to always stay visible on the screen, I guess. Also, if you position your models in, say, meters as opposed to pixels, it's also perfectly fine, you'll just need a slightly different matrix to transform them to normalized coordinates. As far as camera is concerned - again, it's totally up to you to choose, whether to use one or not. I'd say if your screen doesn't move at all, you don't need a camera. Otherwise having a camera can be convenient, as you will always have a point of reference "attached" to the visible part of your world and will be able to transform your scene into normalized view space based on that point of reference. If I'm not much mistaken, for a 2D camera you'll only need to specify the position and the view width and height. Then to transform a point from world to clip space you will just need to translate it by -cameraX, -cameraY and scale by 2/cameraViewWidth, 2/cameraViewHeight. You can think of it as the more general case to the matrix you've constructed, where a camera is positioned at (scr_width/2, scr_height/2) and its view dimensions are (scr_width, scr_height).

Well, hopefully it helps or at least doesn't confuse you even moresmile.png Please correct me if something I've said is utter nonsense, as I can't say I'm 100% confident with the topic eitherrolleyes.gif

This topic is closed to new replies.

Advertisement