SetRenderState(D3DRS_CLIPPING, FALSE) does not disable clipping

Started by
13 comments, last by C0D1F1ED 17 years, 8 months ago
Hi all, I have an application where the viewport is smaller than the entire render target, but I don't want the geometry to be clipped to the viewport. I assumed SetRenderState(D3DRS_CLIPPING, FALSE) would do the trick but it still clips to the viewport. So how do I really disable clipping, and what is D3DRS_CLIPPING actually used for? Thanks, c0d1f1ed
Advertisement
The viewport, by definition, is the active partition of your render target. Vertices are translated and scaled from the projection(screen) space coordinates ([-1,1] in x/y) to screen(device/display) space coordinates, which is a rectangular area of the total render target. So if you set the viewport to,say, the upper right half of the screen, the rendered image will be scaled to fit the rectangle. This translation and scaling occurs independent whether you use fixed or programmable pipeline.

Edit:
D3DRS_CLIPPING on the other hand, is used to enable/disable geometry clipping planes defined in world space. Primitives behind the plane(s) will not be rendered. I mixed this with D3DRS_CLIPPLANEENABLE, which is the world-space clipping stuff. However, neither have anything to do with viewports.

It seems you want to move the center of the view while still rendering to the whole render target area. This can be done either by altering the view or projection matrices with custom translation. This sounds like a good time to brush up your projection matrix math algebra.
Read up on Guard-band clipping. It might also be an issue with a scissor rect.

Does this happen when you run with the Reference Rasterizer?

Quote:Original post by clb
The viewport, by definition, is the active partition of your render target. Vertices are translated and scaled from the projection(screen) space coordinates ([-1,1] in x/y) to screen(device/display) space coordinates, which is a rectangular area of the total render target. So if you set the viewport to,say, the upper right half of the screen, the rendered image will be scaled to fit the rectangle. This translation and scaling occurs independent whether you use fixed or programmable pipeline.

I know. But it's just a scaling of (x, y) from [-1, 1][-1, 1] to the viewport dimensions in screen space, so when clipping is disabled (which prevents coordinates to go outside [-1, 1][-1, 1] before the viewport transform) it should be possible to render outside the viewport.
Quote:Edit:
D3DRS_CLIPPING on the other hand, is used to enable/disable geometry clipping planes defined in world space. Primitives behind the plane(s) will not be rendered. I mixed this with D3DRS_CLIPPLANEENABLE, which is the world-space clipping stuff. However, neither have anything to do with viewports.

So what does D3DRS_CLIPPING really do then? Whether I enable or disable it, my geometry still gets clipped to the viewport.
Quote:It seems you want to...

I'm just exploring the API...

Thanks!
Quote:Original post by don
Read up on Guard-band clipping. It might also be an issue with a scissor rect.

Guard band clipping is an implementation optimization, it doesn't have anything to do with the actual clipping behaviour of the API. I'm not using any scissor rectangles.
Quote:Does this happen when you run with the Reference Rasterizer?

Yes.
Here's a small test application illustrating the behaviour:
#include <d3d9.h>#include <d3dx9.h>HWND hwnd = 0;LPDIRECT3D9 d3d = 0;LPDIRECT3DDEVICE9 device = 0;LPDIRECT3DVERTEXBUFFER9 vertexBuffer = 0;LPDIRECT3DINDEXBUFFER9 indexBuffer = 0;float spinX = 0.0f;float spinY = 0.0f;struct Vertex{    float x;    float y;    float z;    DWORD color;    enum FVF    {        FVF_Flags = D3DFVF_XYZ | D3DFVF_DIFFUSE    };};Vertex cubeVertices[] ={    {-1.0f, 1.0f,-1.0f,  D3DCOLOR_COLORVALUE(1.0, 0.0, 0.0, 1.0) }, // 0    { 1.0f, 1.0f,-1.0f,  D3DCOLOR_COLORVALUE(0.0, 1.0, 0.0, 1.0) }, // 1    {-1.0f,-1.0f,-1.0f,  D3DCOLOR_COLORVALUE(0.0, 0.0, 1.0, 1.0) }, // 2    { 1.0f,-1.0f,-1.0f,  D3DCOLOR_COLORVALUE(1.0, 1.0, 0.0, 1.0) }, // 3    {-1.0f, 1.0f, 1.0f,  D3DCOLOR_COLORVALUE(1.0, 0.0, 1.0, 1.0) }, // 4    {-1.0f,-1.0f, 1.0f,  D3DCOLOR_COLORVALUE(0.0, 1.0, 1.0, 1.0) }, // 5    { 1.0f, 1.0f, 1.0f,  D3DCOLOR_COLORVALUE(1.0, 1.0, 1.0, 1.0) }, // 6    { 1.0f,-1.0f, 1.0f,  D3DCOLOR_COLORVALUE(1.0, 0.0, 0.0, 1.0) }  // 7};WORD cubeIndices[] ={    0, 1, 2, 3, // Quad 0    4, 5, 6, 7, // Quad 1    4, 6, 0, 1, // Quad 2    5, 2, 7, 3, // Quad 3    1, 6, 3, 7, // Quad 4    0, 2, 4, 5  // Quad 5};int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);void init(void);void shutDown(void);void render(void);int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){    WNDCLASSEX windowClass;    MSG message;    memset(&message, 0, sizeof(message));        windowClass.lpszClassName = "MY_WINDOWS_CLASS";    windowClass.cbSize        = sizeof(WNDCLASSEX);    windowClass.style         = CS_HREDRAW | CS_VREDRAW;    windowClass.lpfnWndProc   = WindowProc;    windowClass.hInstance     = hInstance;    windowClass.hIcon	      = 0;    windowClass.hIconSm	      = 0;    windowClass.hCursor       = LoadCursor(0, IDC_ARROW);    windowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);    windowClass.lpszMenuName  = 0;    windowClass.cbClsExtra    = 0;    windowClass.cbWndExtra    = 0;    if(!RegisterClassEx(&windowClass)) return E_FAIL;    hwnd = CreateWindowEx(0, "MY_WINDOWS_CLASS", "Viewport test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 640, 480, 0, 0, hInstance, 0);    if(hwnd == 0) return E_FAIL;    ShowWindow(hwnd, nCmdShow);    UpdateWindow(hwnd);    init();    while(message.message != WM_QUIT)    {        if(PeekMessage(&message, 0, 0, 0, PM_REMOVE))        {             TranslateMessage(&message);            DispatchMessage(&message);        }        else        {            render();        }    }    shutDown();    UnregisterClass("MY_WINDOWS_CLASS", windowClass.hInstance);    return message.wParam;}LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){    static POINT previousMousePosition;    static POINT currentMousePosition;    static bool mousing;    switch(msg)    {	    case WM_KEYDOWN:        if(wParam == VK_ESCAPE)        {            PostQuitMessage(0);        }        break;    case WM_LBUTTONDOWN:        previousMousePosition.x = currentMousePosition.x = LOWORD(lParam);        previousMousePosition.y = currentMousePosition.y = HIWORD(lParam);        mousing = true;        break;    case WM_LBUTTONUP:        mousing = false;        break;    case WM_MOUSEMOVE:        currentMousePosition.x = LOWORD(lParam);        currentMousePosition.y = HIWORD(lParam);        if(mousing)        {            spinX -= (currentMousePosition.x - previousMousePosition.x);            spinY -= (currentMousePosition.y - previousMousePosition.y);        }        previousMousePosition.x = currentMousePosition.x;        previousMousePosition.y = currentMousePosition.y;        break;    case WM_CLOSE:    case WM_DESTROY:        PostQuitMessage(0);        break;    default:        return DefWindowProc(hWnd, msg, wParam, lParam);    }    return 0;}void init(void){    d3d = Direct3DCreate9(D3D_SDK_VERSION);    D3DDISPLAYMODE d3ddm;    d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);    D3DPRESENT_PARAMETERS d3dpp;    ZeroMemory(&d3dpp, sizeof(d3dpp));    d3dpp.Windowed               = TRUE;    d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;    d3dpp.BackBufferFormat       = d3ddm.Format;    d3dpp.EnableAutoDepthStencil = TRUE;    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;    d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_IMMEDIATE;    d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &device);    device->SetRenderState(D3DRS_LIGHTING, FALSE);    device->SetRenderState(D3DRS_ZENABLE, TRUE);    device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);    device->CreateVertexBuffer(8*sizeof(Vertex), D3DUSAGE_WRITEONLY | D3DUSAGE_DONOTCLIP, Vertex::FVF_Flags, D3DPOOL_DEFAULT, &vertexBuffer, 0);    void *vertices = 0;    vertexBuffer->Lock(0, sizeof(cubeVertices), &vertices, 0);    memcpy(vertices, cubeVertices, sizeof(cubeVertices));    vertexBuffer->Unlock();    device->CreateIndexBuffer(24 * sizeof(WORD), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &indexBuffer, 0);        void *indices = 0;    indexBuffer->Lock(0, sizeof(cubeIndices), &indices, 0);    memcpy(indices, cubeIndices, sizeof(cubeIndices));    indexBuffer->Unlock();}void shutDown(void){    if(vertexBuffer != 0) vertexBuffer->Release();     if(indexBuffer != 0) indexBuffer->Release();     if(device != 0) device->Release();    if(d3d != 0) d3d->Release();}void render(void){    D3DVIEWPORT9 viewport;    viewport.X = 0;    viewport.Y = 0;    viewport.Width = 320;    viewport.Height = 480;    viewport.MinZ = 0;    viewport.MaxZ = 1;    device->SetViewport(&viewport);    device->SetRenderState(D3DRS_CLIPPING, FALSE);    D3DXMATRIX matProj;    D3DXMatrixPerspectiveFovLH(&matProj, 45.0f, 640.0f / 480.0f, 0.1f, 100.0f);    // Scale and translate back to the center of the window    matProj._11 *= 2;    matProj._31 = 1;    device->SetTransform(D3DTS_PROJECTION, &matProj);    device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_COLORVALUE(0.0f,0.0f,0.0f,1.0f), 1.0f, 0);    D3DXMATRIX matTrans;    D3DXMATRIX matRot;    D3DXMATRIX matWorld;    D3DXMatrixTranslation(&matTrans, 0.0f, 0.0f, 4.0f);    D3DXMatrixRotationYawPitchRoll(&matRot, D3DXToRadian(spinX), D3DXToRadian(spinY), 0.0f);    matWorld = matRot * matTrans;    device->SetTransform(D3DTS_WORLD, &matWorld);    device->BeginScene();    device->SetStreamSource(0, vertexBuffer, 0, sizeof(Vertex));    device->SetIndices(indexBuffer);    device->SetFVF(Vertex::FVF_Flags);    device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 8,  0, 2);    device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 8,  4, 2);    device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 8,  8, 2);    device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 8, 12, 2);    device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 8, 16, 2);    device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 8, 20, 2);    device->EndScene();    device->Present(0, 0, 0, 0);}

You can spin the cube with the mouse. In render(), the viewport is set to the left half of the window, and clipping is disabled. The perspective transform positions the cube back to the center of the window, but because clipping is somehow still active you can only see the left half of it.
I dont have an installed SDK to hand, so I might be wrong... but I think you'll need to check out the other D3DXMatrixPerspective**() functions.

I've done this before to implement different (but similar in implementation) effects to the one you describe. Post projection transform your geometry will be in -1...+1 coordinates, the viewport matrix will then remap these to your 0..w and 0..h rasterization coordinates.

Off the top of my head, the D3DXMatrixPerspectiveOffCenterLH() (or similar) allows you to specify a top-left and bottom-right rather than just a height/width.

The SDK documentation should have details on the viewport matrix so you can see the mathemagical details if needs be.

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

The guard band was a feature that showed up on pre-TNL hardware that allowed apps to disable software clipping and throw the polys that crossed a viewport edge down to the hardware for rasterization and not have to worry about them being clipped.

As you're probably aware, what you want to do is resize your viewport to the render target rect and then draw your polygons. That will give you the result you seem to want.

Quote:Original post by donAs you're probably aware, what you want to do is resize your viewport to the render target rect and then draw your polygons. That will give you the result you seem to want.

I'm sorry but I don't want a result other than understanding the interaction between viewports and clipping for the Direct3D API. More specifically, I want to know what D3DRS_CLIPPING does or doesn't do. So far it doesn't seem to do anything, while the theory and the API documentation tell me it should allow the geometry to be rendered outside the viewport.
I think I figured it out... D3DRS_CLIPPING is a legacy render state from the times when clipping was still mostly done in software. On modern hardware it's simply ignored and clipping is always enabled. I think this is what you were trying to tell me with the guard-band clipping, but I didn't know that it was always implicitely enabled.

I think it's very odd that they've kept this in Direct3D 9. I don't think there's any hardware with a DX9 driver that can't do hardware clipping. Even if it does exist, I doubt there's any application still doing its own clipping...

This topic is closed to new replies.

Advertisement