1
\$\begingroup\$

I have been implementing soft shadows into my game engine and it all looks ok in the beginning, but when I rotate the camera I get this...
https://puu.sh/rQ7yG/8cd692aaab.png
When I move around I also see that the shadow is offset from the model... https://puu.sh/rQ7B9/6f988749d9.png

Has anyone seen this before or would be able to find out what the issue is? I think it has something to do with the view matrix of the camera or something like that?

For my soft shadows I do 5 passes:

  1. Render the scene from the cameras POV to a texture
  2. Render that texture as black and white
  3. Downscale the black and white texture to half its size
  4. Blur the downscaled texture (blurring the shadows edges)
  5. Upscale the blurred texture back to normal size

I have checked back and all the matrices get updated per frame

Please ask to see any code and I will gladly edit this post with it.

EDIT: Added code

Render Light To Texture

bool CGraphics::RenderToTexture()
{
    D3DXMATRIX worldMatrix,lightViewMatrix,lightProjMatrix;

    bool result;
    CMesh* tempMesh = nullptr;

    m_ToTexture->SetRenderTarget(m_Device->GetDeviceContext());
    m_ToTexture->ClearRenderTarget(m_Device->GetDeviceContext(), 0.0f,0.0f,1.0f,1.0f);

    m_PointLights[0]->GenerateViewMatrix();

    m_Device->GetWorldMatrix(worldMatrix);

    m_PointLights[0]->GetViewMatrix(lightViewMatrix);
    m_PointLights[0]->GetProjMatrix(lightProjMatrix);

    for(int i = 0;i<m_NumModels;i++)
    {
        D3DXVECTOR3 tempPos = m_Models[i]->GetPosition();
        D3DXMatrixTranslation(&worldMatrix,tempPos.x,tempPos.y,tempPos.z);

        result = true;//m_Frustrum->CheckSphere(position.x,position.y,position.z,2.0f);

        if(result)
        {
            m_Models[i]->GetMatrix(worldMatrix);
            tempMesh = m_Models[i]->GetMesh();
            tempMesh->Render(m_Device->GetDeviceContext());

            result = m_DepthShader->Render(m_Device->GetDeviceContext(),tempMesh->GetIndexCount(),
                worldMatrix,lightViewMatrix,lightProjMatrix);
            if(!result) return false;

            m_Device->GetWorldMatrix(worldMatrix);
        }
    }

    m_Device->SetBackBufferTarget();
    m_Device->ResetViewport();

    return true;
}

Render the shadows in black and white

bool CGraphics::RenderBW()
{
    D3DXMATRIX worldMatrix,viewMatrix,projMatrix,lightViewMatrix,lightProjMatrix;

    bool result;
    CMesh* tempMesh = nullptr;

    m_BWTexture->SetRenderTarget(m_Device->GetDeviceContext());
    m_BWTexture->ClearRenderTarget(m_Device->GetDeviceContext(),0.0f,0.0f,1.0f,1.0f);

    m_Cameras[0]->Update();

    m_PointLights[0]->GenerateViewMatrix();

    m_Cameras[0]->GetViewMatrix(viewMatrix);
    m_Device->GetWorldMatrix(worldMatrix);
    m_Device->GetProjMatrix(projMatrix);

    m_PointLights[0]->GetViewMatrix(lightViewMatrix);
    m_PointLights[0]->GetProjMatrix(lightProjMatrix);

    for(int i = 0;i<m_NumModels;i++)
    {
        D3DXVECTOR3 tempPos = m_Models[i]->GetPosition();
        D3DXMatrixTranslation(&worldMatrix,tempPos.x,tempPos.y,tempPos.z);

        result = true;//m_Frustrum->CheckSphere(position.x,position.y,position.z,2.0f);

        if(result)
        {
            m_Models[i]->GetMatrix(worldMatrix);
            tempMesh = m_Models[i]->GetMesh();
            tempMesh->Render(m_Device->GetDeviceContext());

            result = m_ShadowShader->Render(m_Device->GetDeviceContext(),tempMesh->GetIndexCount(),
                worldMatrix,viewMatrix,projMatrix,lightViewMatrix,lightProjMatrix,
                m_ToTexture->GetShaderResourceView(),m_PointLights[0]->GetPosition());
            if(!result) return false;

            m_Device->GetWorldMatrix(worldMatrix);
        }
    }

    m_Device->SetBackBufferTarget();
    m_Device->ResetViewport();

    return true;
}

Downscale the texture to half

bool CGraphics::DownScale()
{
    D3DXMATRIX worldMatrix,baseViewMatrix,orthoMatrix;
    bool result;

    m_DownScale->SetRenderTarget(m_Device->GetDeviceContext());

    m_DownScale->ClearRenderTarget(m_Device->GetDeviceContext(),0.0f,0.0f,0.0f,1.0f);

    m_Cameras[0]->Update();

    m_Device->GetWorldMatrix(worldMatrix);
    m_Cameras[0]->GetBaseViewMatrix(baseViewMatrix);

    // Get the ortho matrix from the render to texture since texture has different dimensions being that it is smaller.
    m_DownScale->GetOrthoMatrix(orthoMatrix);

    m_Device->ZBufferOff();

    m_DownSampleWindow->Render(m_Device->GetDeviceContext());
    result = m_TextureShader->Render(m_Device->GetDeviceContext(),m_DownSampleWindow->GetIndexCount(),
        worldMatrix,baseViewMatrix,orthoMatrix,m_BWTexture->GetShaderResourceView());

    m_Device->ZBufferOn();

    m_Device->SetBackBufferTarget();

    m_Device->ResetViewport();

    return true;
}

Blur Width

bool CGraphics::BlurTextureX()
{
    D3DXMATRIX worldMatrix,baseViewMatrix,orthoMatrix;
    float screenX;
    bool result;

    screenX = (float)(SHADOWMAP_WIDTH / 2);

    m_BlurTextureX->SetRenderTarget(m_Device->GetDeviceContext());

    m_BlurTextureX->ClearRenderTarget(m_Device->GetDeviceContext(),0.0f,0.0f,0.0f,1.0f);

    m_Cameras[0]->Update();

    m_Cameras[0]->GetBaseViewMatrix(baseViewMatrix);
    m_Device->GetWorldMatrix(worldMatrix);

    m_BlurTextureX->GetOrthoMatrix(orthoMatrix);

    m_Device->ZBufferOff();

    m_DownSampleWindow->Render(m_Device->GetDeviceContext());

    // Render the small ortho window using the horizontal blur shader and the down sampled render to texture resource.
    result = m_BlurShaderX->Render(m_Device->GetDeviceContext(),m_DownSampleWindow->GetIndexCount(),worldMatrix,baseViewMatrix,orthoMatrix,
        m_DownScale->GetShaderResourceView(),screenX);
    if(!result) return false;

    m_Device->ZBufferOn();

    m_Device->SetBackBufferTarget();
    m_Device->ResetViewport();

    return true;
}

Blur Height

bool CGraphics::BlurTextureY()
{
    D3DXMATRIX worldMatrix,baseViewMatrix,orthoMatrix;
    float screenY;
    bool result;

    screenY = (float)(SHADOWMAP_HEIGHT / 2);

    m_BlurTextureY->SetRenderTarget(m_Device->GetDeviceContext());

    m_BlurTextureY->ClearRenderTarget(m_Device->GetDeviceContext(),0.0f,0.0f,0.0f,1.0f);

    m_Cameras[0]->Update();

    m_Cameras[0]->GetBaseViewMatrix(baseViewMatrix);
    m_Device->GetWorldMatrix(worldMatrix);

    m_BlurTextureY->GetOrthoMatrix(orthoMatrix);

    m_Device->ZBufferOff();

    m_DownSampleWindow->Render(m_Device->GetDeviceContext());

    // Render the small ortho window using the horizontal blur shader and the down sampled render to texture resource.
    result = m_BlurShaderY->Render(m_Device->GetDeviceContext(),m_DownSampleWindow->GetIndexCount(),worldMatrix,baseViewMatrix,orthoMatrix,
        m_BlurTextureX->GetShaderResourceView(),screenY);
    if(!result) return false;

    m_Device->ZBufferOn();

    m_Device->SetBackBufferTarget();
    m_Device->ResetViewport();

    return true;
}

Rescale to full size

bool CGraphics::UpScale()
{
    D3DXMATRIX worldMatrix,baseViewMatrix,orthoMatrix;
    bool result;

    m_UpScale->SetRenderTarget(m_Device->GetDeviceContext());

    m_UpScale->ClearRenderTarget(m_Device->GetDeviceContext(),0.0f,0.0f,0.0f,1.0f);

    m_Cameras[0]->GetBaseViewMatrix(baseViewMatrix);
    m_Device->GetWorldMatrix(worldMatrix);

    // Get the ortho matrix from the render to texture since texture has different dimensions.
    m_UpScale->GetOrthoMatrix(orthoMatrix);

    m_Device->ZBufferOff();

    m_FullScreenWindow->Render(m_Device->GetDeviceContext());

    // Render the full screen ortho window using the texture shader and the small sized final blurred render to texture resource.
    result = m_TextureShader->Render(m_Device->GetDeviceContext(),m_FullScreenWindow->GetIndexCount(),
        worldMatrix,baseViewMatrix,orthoMatrix,m_BlurTextureY->GetShaderResourceView());
    if(!result) return false;

    m_Device->ZBufferOn();

    m_Device->SetBackBufferTarget();

    m_Device->ResetViewport();

    return true;
}

Here is also the camera functions

void CCamera::Update()
{
    D3DXVECTOR3 up, pos, lookAt;
    float yaw, pitch, roll;
    D3DXMATRIX rotationMatrix;

    //Setup upward vector
    up.x = 0.0f;
    up.y = 1.0f;
    up.z = 0.0f;

    //Set position
    pos.x = m_Pos.x;
    pos.y = m_Pos.y;
    pos.z = m_Pos.z;

    //Set default look direction
    lookAt.x = 0.0f;
    lookAt.y = 0.0f;
    lookAt.z = 1.0f;

    //Set rotations
    pitch   = m_Rot.x * 0.0174532925f;
    yaw     = m_Rot.y * 0.0174532925f;
    roll    = m_Rot.z * 0.0174532925f;

    //Create rotation matrix
    D3DXMatrixRotationYawPitchRoll(&rotationMatrix, yaw, pitch, roll);

    //Transform into rotation matrix
    D3DXVec3TransformCoord(&up, &up, &rotationMatrix);
    D3DXVec3TransformCoord(&lookAt, &lookAt, &rotationMatrix);

    //Translate rotated camera to location of the viewer
    lookAt = pos + lookAt;

    //Create the view matrix
    D3DXMatrixLookAtLH(&m_ViewMatrix, &pos, &lookAt, &up);
}

void CCamera::RenderBaseViewMatrix()
{
    D3DXVECTOR3 up,position,lookAt;
    float radians;


    // Setup the vector that points upwards.
    up.x = 0.0f;
    up.y = 1.0f;
    up.z = 0.0f;

    // Setup the position of the camera in the world.
    position.x = m_Pos.x;
    position.y = m_Pos.y;
    position.z = m_Pos.z;

    // Calculate the rotation in radians.
    radians = m_Rot.y * 0.0174532925f;

    // Setup where the camera is looking.
    lookAt.x = sinf(radians) + m_Pos.x;
    lookAt.y = m_Pos.y;
    lookAt.z = cosf(radians) + m_Pos.z;

    // Create the base view matrix from the three vectors.
    D3DXMatrixLookAtLH(&m_BaseViewMatrix,&position,&lookAt,&up);
}

Anything else?

\$\endgroup\$
  • \$\begingroup\$ Take a look at this tutorial. It is how i learnt how to implement PCFs. It will recommend you also another techniques which you can find helpful. It is in OpenGL, but for learn and understand how it works it is great. youtube.com/… \$\endgroup\$ – Haruko Oct 24 '16 at 1:17
1
\$\begingroup\$

It's not clear that in step 3) you're downscaling your shadow map or your shadowed scene.

The general process of soft shadows:

  1. Render scene from the light's point of view into a texture (called shadow map)

  2. Render the "shadowed scene" into a texture (now you have hard shadows)

  3. Downscale the texture created in step 2)

  4. Apply blur

  5. Upscale the blurred image and render the final image using the blurred texture

You can find a better/longer description in gamedev.net. Also you can use different PCF functions which can produce acceptable results. (Another article with some tips & tricks).

Edit:

Anyway, based on your images, it seems to be a downscale/upscale problem. In addition with DirectX you have to use half-pixel offsets in your shaders.

\$\endgroup\$
  • \$\begingroup\$ Hi, I edited my post to be more clear. \$\endgroup\$ – Tom Royle Oct 21 '16 at 10:14
  • \$\begingroup\$ When you say an upscale downscale problem, what do you think the cause could be? \$\endgroup\$ – Tom Royle Oct 21 '16 at 10:15
  • \$\begingroup\$ I might be wrong about downscale/upscale, I don't have exact idea. :) Is the shadow mapping working without the downscale+blur+upscale part? If it's working then the problem must be there. You should try to find the root cause (a matrix can be wrong, texture sampling can be wrong, downscaling/upscaling/blurring can be wrong). If you could isolate the problem, that would be nice. Unfortunately, I don't have any idea. \$\endgroup\$ – csisy Oct 21 '16 at 10:39
  • \$\begingroup\$ Ok I think I have found the root cause. When I create the texture from the lights POV it uses it fine, but when I rotate the camera I think the texture is also being rotated with everything else. How would I fix this? \$\endgroup\$ – Tom Royle Oct 23 '16 at 22:14
  • \$\begingroup\$ Can't be sure without posting your code. Also, I recommend implementing shadow maps without any additional technique, always start with the simplest "hard shadows". \$\endgroup\$ – csisy Oct 23 '16 at 23:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.