float Float3::Distance(const Float3& a, const Float3& b) { XMVECTOR x = a.ToSIMD(); XMVECTOR y = b.ToSIMD(); XMVECTOR length = XMVector3Length(XMVectorSubtract(x, y)); return XMVectorGetX(length); }
// Renders meshes using cascaded shadow mapping void MeshRenderer::RenderSunShadowMap(ID3D11DeviceContext* context, const Camera& camera) { PIXEvent event(L"Sun Shadow Map Rendering"); const float MinDistance = reductionDepth.x; const float MaxDistance = reductionDepth.y; // Compute the split distances based on the partitioning mode float CascadeSplits[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; { float lambda = 1.0f; float nearClip = camera.NearClip(); float farClip = camera.FarClip(); float clipRange = farClip - nearClip; float minZ = nearClip + MinDistance * clipRange; float maxZ = nearClip + MaxDistance * clipRange; float range = maxZ - minZ; float ratio = maxZ / minZ; for(uint32 i = 0; i < NumCascades; ++i) { float p = (i + 1) / static_cast<float>(NumCascades); float log = minZ * std::pow(ratio, p); float uniform = minZ + range * p; float d = lambda * (log - uniform) + uniform; CascadeSplits[i] = (d - nearClip) / clipRange; } } Float3 c0Extents; Float4x4 c0Matrix; const Float3 lightDir = AppSettings::SunDirection; // Render the meshes to each cascade for(uint32 cascadeIdx = 0; cascadeIdx < NumCascades; ++cascadeIdx) { PIXEvent cascadeEvent((L"Rendering Shadow Map Cascade " + ToString(cascadeIdx)).c_str()); // Set the viewport SetViewport(context, ShadowMapSize, ShadowMapSize); // Set the shadow map as the depth target ID3D11DepthStencilView* dsv = sunShadowDepthMap.DSView; ID3D11RenderTargetView* nullRenderTargets[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = { NULL }; context->OMSetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, nullRenderTargets, dsv); context->ClearDepthStencilView(dsv, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0); // Get the 8 points of the view frustum in world space XMVECTOR frustumCornersWS[8] = { XMVectorSet(-1.0f, 1.0f, 0.0f, 1.0f), XMVectorSet( 1.0f, 1.0f, 0.0f, 1.0f), XMVectorSet( 1.0f, -1.0f, 0.0f, 1.0f), XMVectorSet(-1.0f, -1.0f, 0.0f, 1.0f), XMVectorSet(-1.0f, 1.0f, 1.0f, 1.0f), XMVectorSet( 1.0f, 1.0f, 1.0f, 1.0f), XMVectorSet( 1.0f, -1.0f, 1.0f, 1.0f), XMVectorSet(-1.0f, -1.0f, 1.0f, 1.0f), }; float prevSplitDist = cascadeIdx == 0 ? MinDistance : CascadeSplits[cascadeIdx - 1]; float splitDist = CascadeSplits[cascadeIdx]; XMVECTOR det; XMMATRIX invViewProj = XMMatrixInverse(&det, camera.ViewProjectionMatrix().ToSIMD()); for(uint32 i = 0; i < 8; ++i) frustumCornersWS[i] = XMVector3TransformCoord(frustumCornersWS[i], invViewProj); // Get the corners of the current cascade slice of the view frustum for(uint32 i = 0; i < 4; ++i) { XMVECTOR cornerRay = XMVectorSubtract(frustumCornersWS[i + 4], frustumCornersWS[i]); XMVECTOR nearCornerRay = XMVectorScale(cornerRay, prevSplitDist); XMVECTOR farCornerRay = XMVectorScale(cornerRay, splitDist); frustumCornersWS[i + 4] = XMVectorAdd(frustumCornersWS[i], farCornerRay); frustumCornersWS[i] = XMVectorAdd(frustumCornersWS[i], nearCornerRay); } // Calculate the centroid of the view frustum slice XMVECTOR frustumCenterVec = XMVectorZero(); for(uint32 i = 0; i < 8; ++i) frustumCenterVec = XMVectorAdd(frustumCenterVec, frustumCornersWS[i]); frustumCenterVec = XMVectorScale(frustumCenterVec, 1.0f / 8.0f); Float3 frustumCenter = frustumCenterVec; // Pick the up vector to use for the light camera Float3 upDir = camera.Right(); Float3 minExtents; Float3 maxExtents; { // Create a temporary view matrix for the light Float3 lightCameraPos = frustumCenter; Float3 lookAt = frustumCenter - lightDir; XMMATRIX lightView = XMMatrixLookAtLH(lightCameraPos.ToSIMD(), lookAt.ToSIMD(), upDir.ToSIMD()); // Calculate an AABB around the frustum corners XMVECTOR mins = XMVectorSet(REAL_MAX, REAL_MAX, REAL_MAX, REAL_MAX); XMVECTOR maxes = XMVectorSet(-REAL_MAX, -REAL_MAX, -REAL_MAX, -REAL_MAX); for(uint32 i = 0; i < 8; ++i) { XMVECTOR corner = XMVector3TransformCoord(frustumCornersWS[i], lightView); mins = XMVectorMin(mins, corner); maxes = XMVectorMax(maxes, corner); } minExtents = mins; maxExtents = maxes; } // Adjust the min/max to accommodate the filtering size float scale = (ShadowMapSize + FilterSize) / static_cast<float>(ShadowMapSize); minExtents.x *= scale; minExtents.y *= scale; maxExtents.x *= scale; maxExtents.x *= scale; Float3 cascadeExtents = maxExtents - minExtents; // Get position of the shadow camera Float3 shadowCameraPos = frustumCenter + lightDir * -minExtents.z; // Come up with a new orthographic camera for the shadow caster OrthographicCamera shadowCamera(minExtents.x, minExtents.y, maxExtents.x, maxExtents.y, 0.0f, cascadeExtents.z); shadowCamera.SetLookAt(shadowCameraPos, frustumCenter, upDir); // Draw the mesh with depth only, using the new shadow camera RenderDepth(context, shadowCamera, true, false); // Apply the scale/offset matrix, which transforms from [-1,1] // post-projection space to [0,1] UV space XMMATRIX texScaleBias; texScaleBias.r[0] = XMVectorSet(0.5f, 0.0f, 0.0f, 0.0f); texScaleBias.r[1] = XMVectorSet(0.0f, -0.5f, 0.0f, 0.0f); texScaleBias.r[2] = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); texScaleBias.r[3] = XMVectorSet(0.5f, 0.5f, 0.0f, 1.0f); XMMATRIX shadowMatrix = shadowCamera.ViewProjectionMatrix().ToSIMD(); shadowMatrix = XMMatrixMultiply(shadowMatrix, texScaleBias); // Store the split distance in terms of view space depth const float clipDist = camera.FarClip() - camera.NearClip(); shadowConstants.Data.CascadeSplits[cascadeIdx] = camera.NearClip() + splitDist * clipDist; if(cascadeIdx == 0) { c0Extents = cascadeExtents; c0Matrix = shadowMatrix; shadowConstants.Data.ShadowMatrix = XMMatrixTranspose(shadowMatrix); shadowConstants.Data.CascadeOffsets[0] = Float4(0.0f, 0.0f, 0.0f, 0.0f); shadowConstants.Data.CascadeScales[0] = Float4(1.0f, 1.0f, 1.0f, 1.0f); } else { // Calculate the position of the lower corner of the cascade partition, in the UV space // of the first cascade partition Float4x4 invCascadeMat = Float4x4::Invert(shadowMatrix); Float3 cascadeCorner = Float3::Transform(Float3(0.0f, 0.0f, 0.0f), invCascadeMat); cascadeCorner = Float3::Transform(cascadeCorner, c0Matrix); // Do the same for the upper corner Float3 otherCorner = Float3::Transform(Float3(1.0f, 1.0f, 1.0f), invCascadeMat); otherCorner = Float3::Transform(otherCorner, c0Matrix); // Calculate the scale and offset Float3 cascadeScale = Float3(1.0f, 1.0f, 1.f) / (otherCorner - cascadeCorner); shadowConstants.Data.CascadeOffsets[cascadeIdx] = Float4(-cascadeCorner, 0.0f); shadowConstants.Data.CascadeScales[cascadeIdx] = Float4(cascadeScale, 1.0f); } ConvertToEVSM(context, cascadeIdx, shadowConstants.Data.CascadeScales[cascadeIdx].To3D()); } }
Quaternion Quaternion::FromAxisAngle(const Float3& axis, float angle) { XMVECTOR q = XMQuaternionRotationAxis(axis.ToSIMD(), angle); return Quaternion(q); }
float Float3::Length(const Float3& v) { XMVECTOR x = v.ToSIMD(); XMVECTOR length = XMVector3Length(x); return XMVectorGetX(length); }
Float3 Float3::TransformDirection(const Float3&v, const Float4x4& m) { XMVECTOR vec = v.ToSIMD(); vec = XMVector3TransformNormal(vec, m.ToSIMD()); return Float3(vec); }
Float3 Float3::Transform(const Float3& v, const Float4x4& m) { XMVECTOR vec = v.ToSIMD(); vec = XMVector3TransformCoord(vec, m.ToSIMD()); return Float3(vec); }
Float3 Float3::Normalize(const Float3& a) { Float3 result; XMStoreFloat3(reinterpret_cast<XMFLOAT3*>(&result), XMVector3Normalize(a.ToSIMD())); return result; }
Float3 Float3::Cross(const Float3& a, const Float3& b) { Float3 result; XMStoreFloat3(reinterpret_cast<XMFLOAT3*>(&result), XMVector3Cross(a.ToSIMD(), b.ToSIMD())); return result; }
float Float3::Dot(const Float3& a, const Float3& b) { return XMVectorGetX(XMVector3Dot(a.ToSIMD(), b.ToSIMD())); }