/// <summary> /// 相机位置到物体Box的最大最小值距离( 无穷范数 ) /// </summary> /// <param name="box">AABB数据</param> /// <param name="min">AABB距相机距离最近值</param> /// <param name="max">AABB距相机距离最远值</param> private void MinMax(ref OOBox box, ref float min, ref float max) { min = 0; max = float.MinValue; // 相机到Box中心的向量三维度的距离表示 Vector3 diff = (mPosition - box.Mid).Abs(); // 三维度距离与Box此寸比较,计算最大和最小距离值 for (int i = 0; i < 3; i++) { float d1 = diff[i] - box.Size[i]; if (d1 > min) { min = d1; } float d2 = diff[i] + box.Size[i]; if (d2 > max) { max = d2; } } // 不能小于0, 比如在内部 if (min < 0) { min = 0; } }
/// <summary> /// 将物体保存到 小根堆 /// </summary> /// <param name="obj">待添加物体</param> /// <param name="box">添加物体的Box的大小</param> private void PushBox2(object obj, ref OOBox box) { // 最近和最远平面距离计算 MinMax(ref box, ref box.Zmin, ref box.Zmax); // 以最近距离为权重值,添加到优先队列 mMinQueue.Enqueue(box.Zmin, obj, box.Zmin); }
/// <summary> /// 判断 Box 的可见性 /// </summary> /// <param name="flush">Node结点的Box 为 1, 需要将缓存的遮挡物先绘制再查询; Node中的Item为0.</param> /// <param name="box">AABB数据</param> /// <param name="dist">最近距离( 负无穷范数 )</param> /// <returns></returns> private int IsVisible(int flush, ref OOBox box, float dist) { // Box放大一点点 OOBox q = box; q.Size = box.Size + Vector3.one * mSafeDistance; // 截头体测试 int visible = mFrustum.Test(ref q); if (visible == 0) { return(0); } // 相机在Box内部 if (visible == 2) { return(1); } if (flush != 0) { // 绘制Box最远距离 小于 dist的物体 FlushOccluders(dist); } return(QueryBox(ref box)); }
public int Test(ref OOBox b) { int i; float m, n; Vector3 diff = (b.Mid - mPosition).Abs(); // 相机位置在 b 内部, 返回2 if (diff.Less(b.Size)) { return(2); } // 检测Box与6个平面的相交 for (i = 0; i < 6; i++) { // box 中心点 到 面的有符号距离计算 m = Vector3.Dot(b.Mid, mPlanes[i]) + mPlanes[i][3]; // b 的 size 在 面法向 最大投影距离.必定为一个正值 Vector3 plane = mPlanes[i].Abs(); n = Vector3.Dot(b.Size, plane); // 如果 存在一个面,使得 m 值大于 最大投影值,可以肯定 box 不相较于 截头体 if (m > n) { return(0); } } // 这种情况存在: box 与 截头体相交; box在截头体内部. // 这两种情况,都需要保留 box 对应的 物体 return(1); }
public OOMesh(MeshFilter filter) { MeshFilter = filter; MeshFilter.sharedMesh.RecalculateBounds(); #if TEST_DRAW_ONE Vector3 world1 = new Vector3(1.14f, -0.7f, -5.94f); Vector3 world2 = new Vector3(1.14f, 1.96f, -7.81f); Vector3 world3 = new Vector3(7.28f, -0.7f, -5.94f); Vertices = new Vector3[] { filter.transform.worldToLocalMatrix.MultiplyPoint3x4(world1), filter.transform.worldToLocalMatrix.MultiplyPoint3x4(world2), filter.transform.worldToLocalMatrix.MultiplyPoint3x4(world3) }; world1 = Camera.main.WorldToScreenPoint(world1) * 32; world2 = Camera.main.WorldToScreenPoint(world2) * 32; world3 = Camera.main.WorldToScreenPoint(world3) * 32; DebugUtils.Info("OOModel", string.Format("world1({0}, {1})", world1.x, world1.y)); DebugUtils.Info("OOModel", string.Format("world2({0}, {1})", world2.x, world2.y)); DebugUtils.Info("OOModel", string.Format("world3({0}, {1})", world3.x, world3.y)); Faces = new Vector3i[] { new Vector3i(0, 1, 2) }; #else Vertices = MeshFilter.sharedMesh.vertices; Faces = ArrayToList(MeshFilter.sharedMesh.triangles); #endif NumVert = Vertices.Length; NumFace = Faces.Length; CameraSpaceVertices = new Vector3[NumVert]; ClipSpaceVertices = new Vector4[NumVert]; Bounds b = MeshFilter.sharedMesh.bounds; Box = new OOBox(b.min, b.max); }
/// <summary> /// 按照相机朝向计算距离值 /// </summary> /// <param name="obj">待添加物体</param> /// <param name="box">待添加物体的Box数据</param> private void PushBox(object obj, ref OOBox box) { float dis, d; dis = -Vector3.Dot(box.Mid, mLook); d = Vector3.Dot(mAbsLook, box.Size); box.Zmin = dis - d; box.Zmax = dis + d; mMinQueue.Enqueue(box.Zmin, obj, box.Zmin); }
public OOModel(MannulDraw drawer) { Drawer = drawer; MeshFilter mf = drawer.gameObject.GetComponent <MeshFilter>(); Model = new OOMesh(mf); Box = new OOBox(Vector3.one * float.MaxValue, Vector3.one * float.MinValue); UpdateTransform(); Head = new OOItem(); Tail = new OOItem(); Tail.CNext = null; Head.CPrev = null; Head.CNext = Tail; Tail.CPrev = Head; CanOcclude = 1; // GeoDebugDrawUtils.DrawAABB(Box.Min, Box.Max); }
/// <summary> /// 根据相机位置,获得所在区域,然后获取Box可见面构造的轮廓 /// max /// 6 --------- 7 /// / | / | /// 2 --------- 3 | /// | | | | /// | | | | /// | 4 --------- 5 /// | / | / /// 0 --------- 1 /// min /// </summary> /// <param name="box">查询Box的可见性</param> /// <returns></returns> private int QueryBox(ref OOBox box) { Vector3 min = box.Min; Vector3 max = box.Max; // 8顶点索引和数据对应 Vector4[] vxt = new Vector4[8]; vxt[0] = new Vector4(min[0], min[1], min[2], 1); vxt[1] = new Vector4(max[0], min[1], min[2], 1); vxt[2] = new Vector4(min[0], max[1], min[2], 1); vxt[3] = new Vector4(max[0], max[1], min[2], 1); vxt[4] = new Vector4(min[0], min[1], max[2], 1); vxt[5] = new Vector4(max[0], min[1], max[2], 1); vxt[6] = new Vector4(min[0], max[1], max[2], 1); vxt[7] = new Vector4(max[0], max[1], max[2], 1); // 区域码计算 int cd = 0; if (mPosition[0] < min[0]) { // 左面之左 cd |= 1; } if (mPosition[0] > max[0]) { // 右面之右 cd |= 2; } if (mPosition[1] < min[1]) { // 下面之下 cd |= 4; } if (mPosition[1] > max[1]) { // 上面之上 cd |= 8; } if (mPosition[2] < min[2]) { // 进面之近 cd |= 16; } if (mPosition[2] > max[2]) { // 远面之远 cd |= 32; } // 到这一步,已经过了截头体测试.物体均在相机视角内 // 索引相机能见到的顶点 int[] stt = STAB[cd]; // 数组0索引表示能见到的顶点数量,后面的数组索引记录Box的顶点索引 int vp = stt[0]; #if TEST_DRAW List <Vector3> polygon = new List <Vector3>(); List <Vector3> clipPolygon = new List <Vector3>(); #endif // 遍历顶点 for (int i = 0; i < vp; i++) { // 获得顶点索引 int j = stt[i + 1]; // 将顶点变换到裁剪空间 mClip.mClipSpaceVertices[i] = mPV * vxt[j]; #if TEST_DRAW polygon.Add(vxt[j]); // 裁剪前 clipPolygon.Add(mClip.mClipSpaceVertices[i] * (1 / mClip.mClipSpaceVertices[i][3])); #endif } #if TEST_DRAW // 绘制可见多边形 GeoDebugDrawUtils.DrawPolygon(polygon, Color.red); GeoDebugDrawUtils.DrawPolygon(clipPolygon, Color.red); DrawNDCBox(); #endif // 对Box的轮廓进行裁剪计算,返回裁剪后的顶点数 vp = mClip.ClipAndProject(vp); #if TEST_DRAW clipPolygon.Clear(); // z 方向上 偏移 0.01 Vector4 offset = new Vector4(0, 0, 0.01f, 0); for (int i = 0; i < vp; ++i) { clipPolygon.Add(offset + mClip.mClipSpaceVertices[i]); } // 裁剪后 GeoDebugDrawUtils.DrawPolygon(clipPolygon, Color.blue); #endif if (vp < 3) { return(0); } int res = Map.QueryPolygon(mClip.mScreenSpaceVertices, vp); return(res); }