//----------------------------------------------------------------------- void Frustum::updateWorldSpaceCornersImpl(void) const { Matrix4 eyeToWorld = mViewMatrix.inverseAffine(); //将观察空间转到世界空间的矩阵 // Note: 尽管我们在这里能够用通用投影矩阵处理,但是由于它与无限远平面不兼容,所以我们仍然要用投影参数处理 still need to working with projection parameters. // 计算近剪裁平面的角 Real nearLeft, nearRight, nearBottom, nearTop; calcProjectionParameters(nearLeft, nearRight, nearBottom, nearTop); // 最远剪裁平面距离 Real farDist = (mFarDist == 0) ? 100000 : mFarDist; // 计算平面四角 Real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1; Real farLeft = nearLeft * radio; Real farRight = nearRight * radio; Real farBottom = nearBottom * radio; Real farTop = nearTop * radio; // 近剪裁平面 mWorldSpaceCorners[0] = eyeToWorld.transformAffine(Vector3(nearRight, nearTop, -mNearDist)); mWorldSpaceCorners[1] = eyeToWorld.transformAffine(Vector3(nearLeft, nearTop, -mNearDist)); mWorldSpaceCorners[2] = eyeToWorld.transformAffine(Vector3(nearLeft, nearBottom, -mNearDist)); mWorldSpaceCorners[3] = eyeToWorld.transformAffine(Vector3(nearRight, nearBottom, -mNearDist)); // 远剪裁平面 mWorldSpaceCorners[4] = eyeToWorld.transformAffine(Vector3(farRight, farTop, -farDist)); mWorldSpaceCorners[5] = eyeToWorld.transformAffine(Vector3(farLeft, farTop, -farDist)); mWorldSpaceCorners[6] = eyeToWorld.transformAffine(Vector3(farLeft, farBottom, -farDist)); mWorldSpaceCorners[7] = eyeToWorld.transformAffine(Vector3(farRight, farBottom, -farDist)); mRecalcWorldSpaceCorners = false; }
// ------------------------------------------------------------------- void Camera::setWindowImpl() const { if (!mWindowSet || !mRecalcWindow) return; // Calculate general projection parameters Real vpLeft, vpRight, vpBottom, vpTop; calcProjectionParameters(vpLeft, vpRight, vpBottom, vpTop); Real vpWidth = vpRight - vpLeft; Real vpHeight = vpTop - vpBottom; Real wvpLeft = vpLeft + mWLeft * vpWidth; Real wvpRight = vpLeft + mWRight * vpWidth; Real wvpTop = vpTop - mWTop * vpHeight; Real wvpBottom = vpTop - mWBottom * vpHeight; Vector3 vp_ul (wvpLeft, wvpTop, -mNearDist); Vector3 vp_ur (wvpRight, wvpTop, -mNearDist); Vector3 vp_bl (wvpLeft, wvpBottom, -mNearDist); Vector3 vp_br (wvpRight, wvpBottom, -mNearDist); Matrix4 inv = mViewMatrix.inverseAffine(); Vector3 vw_ul = inv.transformAffine(vp_ul); Vector3 vw_ur = inv.transformAffine(vp_ur); Vector3 vw_bl = inv.transformAffine(vp_bl); Vector3 vw_br = inv.transformAffine(vp_br); mWindowClipPlanes.clear(); if (mProjType == PT_PERSPECTIVE) { Vector3 position = getPositionForViewUpdate(); mWindowClipPlanes.push_back(Plane(position, vw_bl, vw_ul)); mWindowClipPlanes.push_back(Plane(position, vw_ul, vw_ur)); mWindowClipPlanes.push_back(Plane(position, vw_ur, vw_br)); mWindowClipPlanes.push_back(Plane(position, vw_br, vw_bl)); } else { Vector3 x_axis(inv[0][0], inv[0][1], inv[0][2]); Vector3 y_axis(inv[1][0], inv[1][1], inv[1][2]); x_axis.normalise(); y_axis.normalise(); mWindowClipPlanes.push_back(Plane( x_axis, vw_bl)); mWindowClipPlanes.push_back(Plane(-x_axis, vw_ur)); mWindowClipPlanes.push_back(Plane( y_axis, vw_bl)); mWindowClipPlanes.push_back(Plane(-y_axis, vw_ur)); } mRecalcWindow = false; }
//----------------------------------------------------------------------- void Frustum::updateWorldSpaceCornersImpl(void) const { Matrix4 eyeToWorld = mViewMatrix.inverseAffine(); // Note: Even though we can dealing with general projection matrix here, // but because it's incompatibly with infinite far plane, thus, we // still need to working with projection parameters. // Calc near plane corners Real nearLeft, nearRight, nearBottom, nearTop; calcProjectionParameters(nearLeft, nearRight, nearBottom, nearTop); // Treat infinite fardist as some arbitrary far value Real farDist = (mFarDist == 0) ? 100000 : mFarDist; // Calc far palne corners Real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1; Real farLeft = nearLeft * radio; Real farRight = nearRight * radio; Real farBottom = nearBottom * radio; Real farTop = nearTop * radio; // near mWorldSpaceCorners[0] = eyeToWorld.transformAffine(Vector3(nearRight, nearTop, -mNearDist)); mWorldSpaceCorners[1] = eyeToWorld.transformAffine(Vector3(nearLeft, nearTop, -mNearDist)); mWorldSpaceCorners[2] = eyeToWorld.transformAffine(Vector3(nearLeft, nearBottom, -mNearDist)); mWorldSpaceCorners[3] = eyeToWorld.transformAffine(Vector3(nearRight, nearBottom, -mNearDist)); // far mWorldSpaceCorners[4] = eyeToWorld.transformAffine(Vector3(farRight, farTop, -farDist)); mWorldSpaceCorners[5] = eyeToWorld.transformAffine(Vector3(farLeft, farTop, -farDist)); mWorldSpaceCorners[6] = eyeToWorld.transformAffine(Vector3(farLeft, farBottom, -farDist)); mWorldSpaceCorners[7] = eyeToWorld.transformAffine(Vector3(farRight, farBottom, -farDist)); mRecalcWorldSpaceCorners = false; }
void Frustum::updateFrustumImpl(void) const { // 通用计算 Real left, right, bottom, top; #if SAPPHIRE_NO_VIEWPORT_ORIENTATIONMODE == 0 if (mOrientationMode != OR_PORTRAIT) calcProjectionParameters(bottom, top, left, right); else #endif calcProjectionParameters(left, right, bottom, top); //自定义投影矩阵 if (!mCustomProjMatrix) { // 这些代码下面要处理通用的投影参数, glFrustum和glOrtho相似 // 除了除法不手动优化, 所以这些代码多数自我解释 //宽度w的倒数 Real inv_w = 1 / (right - left); //高度h的倒数 Real inv_h = 1 / (top - bottom); //深度d的倒数 Real inv_d = 1 / (mFarDist - mNearDist); // 如果视锥体参数改变,重新计算 if (mProjType == PT_PERSPECTIVE) //透视投影 { // 计算矩阵元素 Real A = 2 * mNearDist * inv_w; Real B = 2 * mNearDist * inv_h; Real C = (right + left) * inv_w; Real D = (top + bottom) * inv_h; Real q, qn; if (mFarDist == 0) { // 无限远平面 q = Frustum::INFINITE_FAR_PLANE_ADJUST - 1; qn = mNearDist * (Frustum::INFINITE_FAR_PLANE_ADJUST - 2); } else { q = -(mFarDist + mNearDist) * inv_d; qn = -2 * (mFarDist * mNearDist) * inv_d; } // NB: 这创建'uniform'的透视投影矩阵,深度范围[-1,1],右手规则 // // [ A 0 C 0 ] // [ 0 B D 0 ] // [ 0 0 q qn ] // [ 0 0 -1 0 ] // // A = 2 * near / (right - left) // B = 2 * near / (top - bottom) // C = (right + left) / (right - left) // D = (top + bottom) / (top - bottom) // q = - (far + near) / (far - near) // qn = - 2 * (far * near) / (far - near) mProjMatrix = Matrix4::ZERO; mProjMatrix[0][0] = A; mProjMatrix[0][2] = C; mProjMatrix[1][1] = B; mProjMatrix[1][2] = D; mProjMatrix[2][2] = q; mProjMatrix[2][3] = qn; mProjMatrix[3][2] = -1; if (mObliqueDepthProjection) //深度投影 { // 变换这个平面的观察空间 // 如果通过相机和返回一个剔除视锥体观察矩阵重载,不能使用getViewMatrix updateView(); Plane plane = mViewMatrix * mObliqueProjPlane; //计算剪切空间对于剪裁平面的角点 // 如 (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) 并且通过乘投影矩阵的逆矩阵变换它到相机空间 /* 通用版本 Vector4 q = matrix.inverse() * Vector4(Math::Sign(plane.normal.x), Math::Sign(plane.normal.y), 1.0f, 1.0f); */ Vector4 qVec; qVec.x = (Math::Sign(plane.normal.x) + mProjMatrix[0][2]) / mProjMatrix[0][0]; qVec.y = (Math::Sign(plane.normal.y) + mProjMatrix[1][2]) / mProjMatrix[1][1]; qVec.z = -1; qVec.w = (1 + mProjMatrix[2][2]) / mProjMatrix[2][3]; // 计算缩放平面向量 Vector4 clipPlane4d(plane.normal.x, plane.normal.y, plane.normal.z, plane.d); Vector4 c = clipPlane4d * (2 / (clipPlane4d.dotProduct(qVec))); // 替换投影矩阵的第三行 mProjMatrix[2][0] = c.x; mProjMatrix[2][1] = c.y; mProjMatrix[2][2] = c.z + 1; mProjMatrix[2][3] = c.w; } } else if (mProjType == PT_ORTHOGRAPHIC) { Real A = 2 * inv_w; Real B = 2 * inv_h; Real C = -(right + left) * inv_w; Real D = -(top + bottom) * inv_h; Real q, qn; if (mFarDist == 0) { // 不能出来无限远平面,要避免除0 q = -Frustum::INFINITE_FAR_PLANE_ADJUST / mNearDist; qn = -Frustum::INFINITE_FAR_PLANE_ADJUST - 1; } else { q = -2 * inv_d; qn = -(mFarDist + mNearDist) * inv_d; } // NB: 创建'uniform'正交投影矩阵,深度范围[-1,1], 右手规范 // // [ A 0 0 C ] // [ 0 B 0 D ] // [ 0 0 q qn ] // [ 0 0 0 1 ] // // A = 2 * / (right - left) // B = 2 * / (top - bottom) // C = - (right + left) / (right - left) // D = - (top + bottom) / (top - bottom) // q = - 2 / (far - near) // qn = - (far + near) / (far - near) mProjMatrix = Matrix4::ZERO; mProjMatrix[0][0] = A; mProjMatrix[0][3] = C; mProjMatrix[1][1] = B; mProjMatrix[1][3] = D; mProjMatrix[2][2] = q; mProjMatrix[2][3] = qn; mProjMatrix[3][3] = 1; } } // #if SAPPHIRE_NO_VIEWPORT_ORIENTATIONMODE == 0 // 处理朝向模式 mProjMatrix = mProjMatrix * Quaternion(Degree(mOrientationMode * 90.f), Vector3::UNIT_Z); #endif //RenderSystem* renderSystem = Root::getSingleton().getRenderSystem(); //指定API //renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRS); //指定GPU编程的API模式 //renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRSDepth, true); // 计算AABB碰撞盒子(本地) // 盒子是从0到-z, 最大范围到远裁平面 // 视锥体最大值 Real farDist = (mFarDist == 0) ? 100000 : mFarDist; // 近剪裁平面的边界 Vector3 min(left, bottom, -farDist); Vector3 max(right, top, 0); if (mCustomProjMatrix) { //有些自定义的投影矩阵能够用不常用的相反设置 // 所以确定AABB是开始就正确的设置 Vector3 tmp = min; min.makeFloor(max); max.makeCeil(tmp); } if (mProjType == PT_PERSPECTIVE) { // 合并远裁平面边界 Real radio = farDist / mNearDist; min.makeFloor(Vector3(left * radio, bottom * radio, -farDist)); max.makeCeil(Vector3(right * radio, top * radio, 0)); } mBoundingBox.setExtents(min, max); mRecalcFrustum = false; //标记视锥体剪裁平面更新 mRecalcFrustumPlanes = true; }
//----------------------------------------------------------------------- void Frustum::updateVertexData(void) const { if (mRecalcVertexData) { if (mVertexData.vertexBufferBinding->getBufferCount() <= 0) { // Initialise vertex & index data mVertexData.vertexDeclaration->addElement(0, 0, VET_FLOAT3, VES_POSITION); mVertexData.vertexCount = 32; mVertexData.vertexStart = 0; mVertexData.vertexBufferBinding->setBinding( 0, HardwareBufferManager::getSingleton().createVertexBuffer( sizeof(float)*3, 32, HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY) ); } // Note: Even though we can dealing with general projection matrix here, // but because it's incompatibly with infinite far plane, thus, we // still need to working with projection parameters. // Calc near plane corners Real vpLeft, vpRight, vpBottom, vpTop; calcProjectionParameters(vpLeft, vpRight, vpBottom, vpTop); // Treat infinite fardist as some arbitrary far value Real farDist = (mFarDist == 0) ? 100000 : mFarDist; // Calc far palne corners Real radio = mProjType == PT_PERSPECTIVE ? farDist / mNearDist : 1; Real farLeft = vpLeft * radio; Real farRight = vpRight * radio; Real farBottom = vpBottom * radio; Real farTop = vpTop * radio; // Calculate vertex positions (local) // 0 is the origin // 1, 2, 3, 4 are the points on the near plane, top left first, clockwise // 5, 6, 7, 8 are the points on the far plane, top left first, clockwise HardwareVertexBufferSharedPtr vbuf = mVertexData.vertexBufferBinding->getBuffer(0); float* pFloat = static_cast<float*>(vbuf->lock(HardwareBuffer::HBL_DISCARD)); // near plane (remember frustum is going in -Z direction) *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist; *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist; *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist; *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist; // far plane (remember frustum is going in -Z direction) *pFloat++ = farLeft; *pFloat++ = farTop; *pFloat++ = -farDist; *pFloat++ = farRight; *pFloat++ = farTop; *pFloat++ = -farDist; *pFloat++ = farRight; *pFloat++ = farTop; *pFloat++ = -farDist; *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist; *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist; *pFloat++ = farLeft; *pFloat++ = farBottom; *pFloat++ = -farDist; *pFloat++ = farLeft; *pFloat++ = farBottom; *pFloat++ = -farDist; *pFloat++ = farLeft; *pFloat++ = farTop; *pFloat++ = -farDist; // Sides of the pyramid *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist; *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist; *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = 0.0f; *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; // Sides of the box *pFloat++ = vpLeft; *pFloat++ = vpTop; *pFloat++ = -mNearDist; *pFloat++ = farLeft; *pFloat++ = farTop; *pFloat++ = -farDist; *pFloat++ = vpRight; *pFloat++ = vpTop; *pFloat++ = -mNearDist; *pFloat++ = farRight; *pFloat++ = farTop; *pFloat++ = -farDist; *pFloat++ = vpRight; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; *pFloat++ = farRight; *pFloat++ = farBottom; *pFloat++ = -farDist; *pFloat++ = vpLeft; *pFloat++ = vpBottom; *pFloat++ = -mNearDist; *pFloat++ = farLeft; *pFloat++ = farBottom; *pFloat++ = -farDist; vbuf->unlock(); mRecalcVertexData = false; } }
//----------------------------------------------------------------------- void Frustum::updateFrustumImpl(void) const { // Common calcs Real left, right, bottom, top; calcProjectionParameters(left, right, bottom, top); if (!mCustomProjMatrix) { // The code below will dealing with general projection // parameters, similar glFrustum and glOrtho. // Doesn't optimise manually except division operator, so the // code more self-explaining. Real inv_w = 1 / (right - left); Real inv_h = 1 / (top - bottom); Real inv_d = 1 / (mFarDist - mNearDist); // Recalc if frustum params changed if (mProjType == PT_PERSPECTIVE) { // Calc matrix elements Real A = 2 * mNearDist * inv_w; Real B = 2 * mNearDist * inv_h; Real C = (right + left) * inv_w; Real D = (top + bottom) * inv_h; Real q, qn; if (mFarDist == 0) { // Infinite far plane q = Frustum::INFINITE_FAR_PLANE_ADJUST - 1; qn = mNearDist * (Frustum::INFINITE_FAR_PLANE_ADJUST - 2); } else { q = - (mFarDist + mNearDist) * inv_d; qn = -2 * (mFarDist * mNearDist) * inv_d; } // NB: This creates 'uniform' perspective projection matrix, // which depth range [-1,1], right-handed rules // // [ A 0 C 0 ] // [ 0 B D 0 ] // [ 0 0 q qn ] // [ 0 0 -1 0 ] // // A = 2 * near / (right - left) // B = 2 * near / (top - bottom) // C = (right + left) / (right - left) // D = (top + bottom) / (top - bottom) // q = - (far + near) / (far - near) // qn = - 2 * (far * near) / (far - near) mProjMatrix = Matrix4::ZERO; mProjMatrix[0][0] = A; mProjMatrix[0][2] = C; mProjMatrix[1][1] = B; mProjMatrix[1][2] = D; mProjMatrix[2][2] = q; mProjMatrix[2][3] = qn; mProjMatrix[3][2] = -1; if (mObliqueDepthProjection) { // Translate the plane into view space // Don't use getViewMatrix here, incase overrided by // camera and return a cull frustum view matrix updateView(); Plane plane = mViewMatrix * mObliqueProjPlane; // Thanks to Eric Lenyel for posting this calculation // at www.terathon.com // Calculate the clip-space corner point opposite the // clipping plane // as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and // transform it into camera space by multiplying it // by the inverse of the projection matrix /* generalised version Vector4 q = matrix.inverse() * Vector4(Math::Sign(plane.normal.x), Math::Sign(plane.normal.y), 1.0f, 1.0f); */ Vector4 q; q.x = (Math::Sign(plane.normal.x) + mProjMatrix[0][2]) / mProjMatrix[0][0]; q.y = (Math::Sign(plane.normal.y) + mProjMatrix[1][2]) / mProjMatrix[1][1]; q.z = -1; q.w = (1 + mProjMatrix[2][2]) / mProjMatrix[2][3]; // Calculate the scaled plane vector Vector4 clipPlane4d(plane.normal.x, plane.normal.y, plane.normal.z, plane.d); Vector4 c = clipPlane4d * (2 / (clipPlane4d.dotProduct(q))); // Replace the third row of the projection matrix mProjMatrix[2][0] = c.x; mProjMatrix[2][1] = c.y; mProjMatrix[2][2] = c.z + 1; mProjMatrix[2][3] = c.w; } } // perspective else if (mProjType == PT_ORTHOGRAPHIC) { Real A = 2 * inv_w; Real B = 2 * inv_h; Real C = - (right + left) * inv_w; Real D = - (top + bottom) * inv_h; Real q, qn; if (mFarDist == 0) { // Can not do infinite far plane here, avoid divided zero only q = - Frustum::INFINITE_FAR_PLANE_ADJUST / mNearDist; qn = - Frustum::INFINITE_FAR_PLANE_ADJUST - 1; } else { q = - 2 * inv_d; qn = - (mFarDist + mNearDist) * inv_d; } // NB: This creates 'uniform' orthographic projection matrix, // which depth range [-1,1], right-handed rules // // [ A 0 0 C ] // [ 0 B 0 D ] // [ 0 0 q qn ] // [ 0 0 0 1 ] // // A = 2 * / (right - left) // B = 2 * / (top - bottom) // C = - (right + left) / (right - left) // D = - (top + bottom) / (top - bottom) // q = - 2 / (far - near) // qn = - (far + near) / (far - near) mProjMatrix = Matrix4::ZERO; mProjMatrix[0][0] = A; mProjMatrix[0][3] = C; mProjMatrix[1][1] = B; mProjMatrix[1][3] = D; mProjMatrix[2][2] = q; mProjMatrix[2][3] = qn; mProjMatrix[3][3] = 1; } // ortho } // !mCustomProjMatrix RenderSystem* renderSystem = Root::getSingleton().getRenderSystem(); // API specific renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRS); // API specific for Gpu Programs renderSystem->_convertProjectionMatrix(mProjMatrix, mProjMatrixRSDepth, true); // Calculate bounding box (local) // Box is from 0, down -Z, max dimensions as determined from far plane // If infinite view frustum just pick a far value Real farDist = (mFarDist == 0) ? 100000 : mFarDist; // Near plane bounds Vector3 min(left, bottom, -farDist); Vector3 max(right, top, 0); if (mCustomProjMatrix) { // Some custom projection matrices can have unusual inverted settings // So make sure the AABB is the right way around to start with Vector3 tmp = min; min.makeFloor(max); max.makeCeil(tmp); } if (mProjType == PT_PERSPECTIVE) { // Merge with far plane bounds Real radio = farDist / mNearDist; min.makeFloor(Vector3(left * radio, bottom * radio, -farDist)); max.makeCeil(Vector3(right * radio, top * radio, 0)); } mBoundingBox.setExtents(min, max); mRecalcFrustum = false; // Signal to update frustum clipping planes mRecalcFrustumPlanes = true; }