OpenTK/OpenGL Frustum Culling Clipping Too Soon
我最近在我的体素游戏中添加了平截锥体剔除功能;乍一看效果很好。但是,我立即注意到,视锥看起来似乎有点变了,因为窗口边缘附近的一些体素被过早切除,然后完全脱离了视觉范围。这是说明我的努力的图像:
这是解决此问题可能需要的三个类。
我的FrustumWidget类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | using System; using OpenTK; namespace GameProject.Game.Client.Widgets { public class FrustumWidget { private float[] _clipMatrix = new float[ 16 ]; private float[ , ] _frustum = new float[ 6 , 4 ]; const int RIGHT = 0 , LEFT = 1 , BOTTOM = 2 , TOP = 3 , BACK = 4 , FRONT = 5; private void NormalizePlane( float[ , ] frustum , int side ) { float magnitude = ( float )Math.Sqrt( ( frustum[ side , 0 ] * frustum[ side , 0 ] ) + ( frustum[ side , 1 ] * frustum[ side , 1 ] ) + ( frustum[ side , 2 ] * frustum[ side , 2 ] ) ); frustum[ side , 0 ] /= magnitude; frustum[ side , 1 ] /= magnitude; frustum[ side , 2 ] /= magnitude; frustum[ side , 3 ] /= magnitude; } public bool SphereInFrustum( float x , float y , float z , float radius ) { float d = 0; for( int p = 0; p < 6; p++ ) { d = _frustum[ p , 0 ] * x + _frustum[ p , 1 ] * y + _frustum[ p , 2 ] * z + _frustum[ p , 3 ]; if( d <= -radius ) { return false; } } return true; } public bool VoxelWithinFrustum( float x1 , float y1 , float z1 , float x2 , float y2 , float z2 ) { for( int i = 0; i < 6; i++ ) { if( ( this._frustum[ i , 0 ] * x1 + this._frustum[ i , 1 ] * y1 + this._frustum[ i , 2 ] * z1 + this._frustum[ i, 3 ] <= 0.0F ) && ( this._frustum[ i , 0 ] * x2 + this._frustum[ i , 1 ] * y1 + this._frustum[ i , 2 ] * z1 + this._frustum[ i , 3 ] <= 0.0F ) && ( this._frustum[ i , 0 ] * x1 + this._frustum[ i , 1 ] * y2 + this._frustum[ i , 2 ] * z1 + this._frustum[ i , 3 ] <= 0.0F ) && ( this._frustum[ i , 0 ] * x2 + this._frustum[ i , 1 ] * y2 + this._frustum[ i , 2 ] * z1 + this._frustum[ i , 3 ] <= 0.0F ) && ( this._frustum[ i , 0 ] * x1 + this._frustum[ i , 1 ] * y1 + this._frustum[ i , 2 ] * z2 + this._frustum[ i , 3 ] <= 0.0F ) && ( this._frustum[ i , 0 ] * x2 + this._frustum[ i , 1 ] * y1 + this._frustum[ i , 2 ] * z2 + this._frustum[ i , 3 ] <= 0.0F ) && ( this._frustum[ i , 0 ] * x1 + this._frustum[ i , 1 ] * y2 + this._frustum[ i , 2 ] * z2 + this._frustum[ i , 3 ] <= 0.0F ) && ( this._frustum[ i , 0 ] * x2 + this._frustum[ i , 1 ] * y2 + this._frustum[ i , 2 ] * z2 + this._frustum[ i , 3 ] <= 0.0F ) ) { return false; } } return true; } public void CalculateFrustum( Matrix4 projectionMatrix , Matrix4 modelViewMatrix ) { _clipMatrix[ 0 ] = ( modelViewMatrix.M11 * projectionMatrix.M11 ) + ( modelViewMatrix.M12 * projectionMatrix.M21 ) + ( modelViewMatrix.M13 * projectionMatrix.M31 ) + ( modelViewMatrix.M14 * projectionMatrix.M41 ); _clipMatrix[ 1 ] = ( modelViewMatrix.M11 * projectionMatrix.M12 ) + ( modelViewMatrix.M12 * projectionMatrix.M22 ) + ( modelViewMatrix.M13 * projectionMatrix.M32 ) + ( modelViewMatrix.M14 * projectionMatrix.M42 ); _clipMatrix[ 2 ] = ( modelViewMatrix.M11 * projectionMatrix.M13 ) + ( modelViewMatrix.M12 * projectionMatrix.M23 ) + ( modelViewMatrix.M13 * projectionMatrix.M33 ) + ( modelViewMatrix.M14 * projectionMatrix.M43 ); _clipMatrix[ 3 ] = ( modelViewMatrix.M11 * projectionMatrix.M14 ) + ( modelViewMatrix.M12 * projectionMatrix.M24 ) + ( modelViewMatrix.M13 * projectionMatrix.M34 ) + ( modelViewMatrix.M14 * projectionMatrix.M44 ); _clipMatrix[ 4 ] = ( modelViewMatrix.M21 * projectionMatrix.M11 ) + ( modelViewMatrix.M22 * projectionMatrix.M21 ) + ( modelViewMatrix.M23 * projectionMatrix.M31 ) + ( modelViewMatrix.M24 * projectionMatrix.M41 ); _clipMatrix[ 5 ] = ( modelViewMatrix.M21 * projectionMatrix.M12 ) + ( modelViewMatrix.M22 * projectionMatrix.M22 ) + ( modelViewMatrix.M23 * projectionMatrix.M32 ) + ( modelViewMatrix.M24 * projectionMatrix.M42 ); _clipMatrix[ 6 ] = ( modelViewMatrix.M21 * projectionMatrix.M13 ) + ( modelViewMatrix.M22 * projectionMatrix.M23 ) + ( modelViewMatrix.M23 * projectionMatrix.M33 ) + ( modelViewMatrix.M24 * projectionMatrix.M43 ); _clipMatrix[ 7 ] = ( modelViewMatrix.M21 * projectionMatrix.M14 ) + ( modelViewMatrix.M22 * projectionMatrix.M24 ) + ( modelViewMatrix.M23 * projectionMatrix.M34 ) + ( modelViewMatrix.M24 * projectionMatrix.M44 ); _clipMatrix[ 8 ] = ( modelViewMatrix.M31 * projectionMatrix.M11 ) + ( modelViewMatrix.M32 * projectionMatrix.M21 ) + ( modelViewMatrix.M33 * projectionMatrix.M31 ) + ( modelViewMatrix.M34 * projectionMatrix.M41 ); _clipMatrix[ 9 ] = ( modelViewMatrix.M31 * projectionMatrix.M12 ) + ( modelViewMatrix.M32 * projectionMatrix.M22 ) + ( modelViewMatrix.M33 * projectionMatrix.M32 ) + ( modelViewMatrix.M34 * projectionMatrix.M42 ); _clipMatrix[ 10 ] = ( modelViewMatrix.M31 * projectionMatrix.M13 ) + ( modelViewMatrix.M32 * projectionMatrix.M23 ) + ( modelViewMatrix.M33 * projectionMatrix.M33 ) + ( modelViewMatrix.M34 * projectionMatrix.M43 ); _clipMatrix[ 11 ] = ( modelViewMatrix.M31 * projectionMatrix.M14 ) + ( modelViewMatrix.M32 * projectionMatrix.M24 ) + ( modelViewMatrix.M33 * projectionMatrix.M34 ) + ( modelViewMatrix.M34 * projectionMatrix.M44 ); _clipMatrix[ 12 ] = ( modelViewMatrix.M41 * projectionMatrix.M11 ) + ( modelViewMatrix.M42 * projectionMatrix.M21 ) + ( modelViewMatrix.M43 * projectionMatrix.M31 ) + ( modelViewMatrix.M44 * projectionMatrix.M41 ); _clipMatrix[ 13 ] = ( modelViewMatrix.M41 * projectionMatrix.M12 ) + ( modelViewMatrix.M42 * projectionMatrix.M22 ) + ( modelViewMatrix.M43 * projectionMatrix.M32 ) + ( modelViewMatrix.M44 * projectionMatrix.M42 ); _clipMatrix[ 14 ] = ( modelViewMatrix.M41 * projectionMatrix.M13 ) + ( modelViewMatrix.M42 * projectionMatrix.M23 ) + ( modelViewMatrix.M43 * projectionMatrix.M33 ) + ( modelViewMatrix.M44 * projectionMatrix.M43 ); _clipMatrix[ 15 ] = ( modelViewMatrix.M41 * projectionMatrix.M14 ) + ( modelViewMatrix.M42 * projectionMatrix.M24 ) + ( modelViewMatrix.M43 * projectionMatrix.M34 ) + ( modelViewMatrix.M44 * projectionMatrix.M44 ); _frustum[ RIGHT , 0 ] = _clipMatrix[ 3 ] - _clipMatrix[ 0 ]; _frustum[ RIGHT , 1 ] = _clipMatrix[ 7 ] - _clipMatrix[ 4 ]; _frustum[ RIGHT , 2 ] = _clipMatrix[ 11 ] - _clipMatrix[ 8 ]; _frustum[ RIGHT , 3 ] = _clipMatrix[ 15 ] - _clipMatrix[ 12 ]; NormalizePlane( _frustum , RIGHT ); _frustum[ LEFT , 0 ] = _clipMatrix[ 3 ] + _clipMatrix[ 0 ]; _frustum[ LEFT , 1 ] = _clipMatrix[ 7 ] + _clipMatrix[ 4 ]; _frustum[ LEFT , 2 ] = _clipMatrix[ 11 ] + _clipMatrix[ 8 ]; _frustum[ LEFT , 3 ] = _clipMatrix[ 15 ] + _clipMatrix[ 12 ]; NormalizePlane( _frustum , LEFT ); _frustum[ BOTTOM , 0 ] = _clipMatrix[ 3 ] + _clipMatrix[ 1 ]; _frustum[ BOTTOM , 1 ] = _clipMatrix[ 7 ] + _clipMatrix[ 5 ]; _frustum[ BOTTOM , 2 ] = _clipMatrix[ 11 ] + _clipMatrix[ 9 ]; _frustum[ BOTTOM , 3 ] = _clipMatrix[ 15 ] + _clipMatrix[ 13 ]; NormalizePlane( _frustum , BOTTOM ); _frustum[ TOP , 0 ] = _clipMatrix[ 3 ] - _clipMatrix[ 1 ]; _frustum[ TOP , 1 ] = _clipMatrix[ 7 ] - _clipMatrix[ 5 ]; _frustum[ TOP , 2 ] = _clipMatrix[ 11 ] - _clipMatrix[ 9 ]; _frustum[ TOP , 3 ] = _clipMatrix[ 15 ] - _clipMatrix[ 13 ]; NormalizePlane( _frustum , TOP ); _frustum[ BACK , 0 ] = _clipMatrix[ 3 ] - _clipMatrix[ 2 ]; _frustum[ BACK , 1 ] = _clipMatrix[ 7 ] - _clipMatrix[ 6 ]; _frustum[ BACK , 2 ] = _clipMatrix[ 11 ] - _clipMatrix[ 10 ]; _frustum[ BACK , 3 ] = _clipMatrix[ 15 ] - _clipMatrix[ 14 ]; NormalizePlane( _frustum , BACK ); _frustum[ FRONT , 0 ] = _clipMatrix[ 3 ] + _clipMatrix[ 2 ]; _frustum[ FRONT , 1 ] = _clipMatrix[ 7 ] + _clipMatrix[ 6 ]; _frustum[ FRONT , 2 ] = _clipMatrix[ 11 ] + _clipMatrix[ 10 ]; _frustum[ FRONT , 3 ] = _clipMatrix[ 15 ] + _clipMatrix[ 14 ]; NormalizePlane( _frustum , FRONT ); } } } |
我的FirstPersonCameraWidget类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | using System; using System.Drawing; using OpenTK; using GameProject.Game.Framework.OpenTKExtensions; using OpenTK.Graphics.OpenGL; using OpenTK.Input; namespace GameProject.Game.Client.Widgets { public class FirstPersonCameraWidget { public Matrix4 CameraMatrix; public Vector3 Location; public FrustumWidget Frustum; public float Pitch; public float Yaw; public float MoveSpeed; public float LookSpeed; public Vector2 MouseSpeed; public bool IsLooking; public float NearZ; public float FarZ; public FirstPersonCameraWidget(Vector3 cameraOrigin , float pitch , float yaw , float movementSpeed , float lookSpeed ) { CameraMatrix = Matrix4.Identity; this.Location = cameraOrigin; this.Pitch = pitch; this.Yaw = yaw; this.LookSpeed = lookSpeed; this.MoveSpeed = movementSpeed; Frustum = new FrustumWidget(); } public void Update() { if( IsLooking ) { // Possible redundant method? } } public void ProcessMovement( KeyboardState keyboardState , FrameEventArgs e ) { if( keyboardState[ Key.W ] ) { MoveForward( e ); } if( keyboardState[ Key.S ] ) { MoveBackward( e ); } if( keyboardState[ Key.A ] ) { StrafeLeft( e ); } if( keyboardState[ Key.D ] ) { StrafeRight( e ); } if( keyboardState[ Key.Space ] ) { FlyUp( e ); } if( keyboardState[ Key.LShift ] || keyboardState[ Key.RShift ] ) { FlyDown( e ); } } public void MoveForward( FrameEventArgs e ) { Location.X += ( float )Math.Cos( Yaw ) * MoveSpeed * ( float )e.Time; //CameraPosition.Y += ( float )Math.Tan( Pitch ) * MoveSpeed * ( float )e.Time; Location.Z += ( float )Math.Sin( Yaw ) * MoveSpeed * ( float )e.Time; } public void MoveBackward( FrameEventArgs e ) { Location.X -= ( float )Math.Cos( Yaw ) * MoveSpeed * ( float )e.Time; //CameraPosition.Y -= ( float )Math.Tan( Pitch ) * MoveSpeed * ( float )e.Time; Location.Z -= ( float )Math.Sin( Yaw ) * MoveSpeed * ( float )e.Time; } public void StrafeLeft( FrameEventArgs e ) { Location.X -= ( float )Math.Cos( Yaw + Math.PI / 2 ) * MoveSpeed * ( float )e.Time; Location.Z -= ( float )Math.Sin( Yaw + Math.PI / 2 ) * MoveSpeed * ( float )e.Time; } public void StrafeRight( FrameEventArgs e ) { Location.X += ( float )Math.Cos( Yaw + Math.PI / 2 ) * MoveSpeed * ( float )e.Time; Location.Z += ( float )Math.Sin( Yaw + Math.PI / 2 ) * MoveSpeed * ( float )e.Time; } public void FlyUp( FrameEventArgs e ) { Location.Y += MoveSpeed * ( float )e.Time; } public void FlyDown( FrameEventArgs e ) { Location.Y -= MoveSpeed * ( float )e.Time; } public void LookThrough( GameWindow window , Point mouseLocation , FrameEventArgs e ) { if( IsLooking ) { Point windowCenter = new Point( window.Bounds.Left + window.Bounds.Width / 2 , window.Bounds.Top + window.Bounds.Height / 2 ); Vector2 MouseDelta = new Vector2( mouseLocation.X - window.PointToClient( windowCenter ).X , mouseLocation.Y - window.PointToClient( windowCenter ).Y ); System.Windows.Forms.Cursor.Position = windowCenter; MouseSpeed.X *= LookSpeed; MouseSpeed.Y *= LookSpeed; MouseSpeed.X += MouseDelta.X / 40.0f * ( float )e.Time; MouseSpeed.Y += MouseDelta.Y / 40.0f * ( float )e.Time; Yaw += MouseSpeed.X; Pitch -= MouseSpeed.Y; if( Pitch <= -( ( OpenTKExtensions.HalfPI * 2 ) * 0.5f ) + 0.01f ) { Pitch = -( ( OpenTKExtensions.HalfPI * 2 ) * 0.5f ) + 0.01f; } if( Pitch >= ( ( OpenTKExtensions.HalfPI * 2 ) * 0.5f ) - 0.01f ) { Pitch = ( ( OpenTKExtensions.HalfPI * 2 ) * 0.5f ) - 0.01f; } } Vector3 LookAtPoint = new Vector3( ( float )Math.Cos( Yaw ) , ( float )Math.Tan( Pitch ) , ( float )Math.Sin( Yaw ) ); CameraMatrix = Matrix4.LookAt( Location , Location + LookAtPoint , Vector3.UnitY ); /** * Recalculate the Viewport Frustum */ Matrix4 projection = new Matrix4(); GL.GetFloat(GetPName.ProjectionMatrix, out projection); Matrix4 modelview = new Matrix4(); GL.GetFloat(GetPName.ModelviewMatrix, out modelview); Frustum.CalculateFrustum(projection,modelview); } } } |
最后,我的GameClient类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | using System; using System.Collections.Generic; using System.Drawing; using System.Threading.Tasks; using GameProject.Game.Framework.DataStructure; using GameProject.Game.Framework.Generators; using GameProject.Game.Framework.Geometry; using System.Diagnostics; using GameProject.Game.Framework.OpenGLExtensions; using OpenTK; using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; using OpenTK.Input; using GameProject.Game.Client.Widgets; using QuickFont; namespace GameProject.Game.Client { public class GameClient : GameWindow { /** * Client Variables */ public const String CLIENT_VERSION ="v0.0.1"; public const String GAME_NAME ="Voxelbyte"; public FirstPersonCameraWidget GameCamera; private Point _mousePosition; private Point _lastMousePosition; private bool _centerCursorAfterRelease = false; /** * Game Variables */ public VoxelWorld World; public List<GameSelection> SelectionMemory; public QFont DeveloperText; public GameClient() : base( 800 , 600 , GraphicsMode.Default , GAME_NAME ) { GL.Enable( EnableCap.DepthTest ); GL.ClearColor( 0.3f , 0.3f , 0.3f , 0.0f ); } protected override void OnLoad( EventArgs e ) { base.OnLoad( e ); DeveloperText = new QFont("Resources/Fonts/times.ttf" , 14 , FontStyle.Regular ); //TestFont.Options.UseDefaultBlendFunction = false; //this.VSync = VSyncMode.On; SelectionMemory = new List<GameSelection>(); GameCamera = new FirstPersonCameraWidget( new Vector3( 30.0f , 8.0f , 30.0f ) , 0.0f , 0.0f , 15.0f , 0.4f ); //this.WindowState = WindowState.Fullscreen; GL.CullFace( CullFaceMode.Back ); GL.Enable( EnableCap.CullFace ); GL.BlendFunc( BlendingFactorSrc.SrcAlpha , BlendingFactorDest.OneMinusSrcAlpha ); GL.Enable( EnableCap.Blend ); // Generate Map And Log the Time World = BattleWorldReader.ReadFromImage("ColoredPyramid.png" ); Stopwatch sw = new Stopwatch(); sw.Start(); World.Rebuild(); sw.Stop(); Console.WriteLine("World Generation+Optimization took: {0}ms" , sw.ElapsedMilliseconds ); //GLExtensions.EnableWireframe(); } protected override void OnMouseMove( MouseMoveEventArgs e ) { base.OnMouseMove( e ); _mousePosition = new Point( e.X , e.Y ); if( e.Mouse.IsButtonDown( MouseButton.Right ) ) { } } protected override void OnMouseDown( MouseButtonEventArgs e ) { base.OnMouseDown( e ); if( e.Mouse.IsButtonDown( MouseButton.Right ) ) { Point nativeMouse = System.Windows.Forms.Cursor.Position; _lastMousePosition = nativeMouse; Point windowCenter = new Point( Bounds.Left + Bounds.Width / 2 , Bounds.Top + Bounds.Height / 2 ); System.Windows.Forms.Cursor.Position = windowCenter; GameCamera.IsLooking = true; this.CursorVisible = false; } } protected override void OnMouseUp( MouseButtonEventArgs e ) { base.OnMouseUp( e ); if( e.Button == MouseButton.Right ) { GameCamera.IsLooking = false; if( !_centerCursorAfterRelease ) { System.Windows.Forms.Cursor.Position = _lastMousePosition; } this.CursorVisible = true; } } protected override void OnRenderFrame( FrameEventArgs e ) { base.OnRenderFrame( e ); GameCamera.LookThrough( this , _mousePosition , e ); GL.MatrixMode( MatrixMode.Modelview ); GL.Clear( ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit ); GL.LoadMatrix( ref GameCamera.CameraMatrix ); // // GL.EnableClientState( ArrayCap.VertexArray ); GL.EnableClientState( ArrayCap.TextureCoordArray ); GL.DisableClientState( ArrayCap.ColorArray ); int numberOfVoxelsRendered = 0; foreach( Voxel voxel in World.Voxels ) { if( GameCamera.Frustum.VoxelWithinFrustum( voxel.Location.X, voxel.Location.Y, voxel.Location.Z, 1.0f,1.0f,1.0f ) ) { voxel.Render( GameCamera ); numberOfVoxelsRendered++; } } GL.DisableClientState( ArrayCap.VertexArray ); GL.DisableClientState( ArrayCap.TextureCoordArray ); GL.DisableClientState( ArrayCap.ColorArray ); // Render Dev Hud RenderDeveloperHud(); // SwapBuffers(); this.Title = GAME_NAME +" FPS:" + ( int )this.RenderFrequency +" Voxels:" + numberOfVoxelsRendered; } private void RenderDeveloperHud() { QFont.Begin(); GL.PushMatrix(); GL.Translate( 0.0f , 5 , 0f ); DeveloperText.Print("Voxelbyte" ); GL.Translate( 0f , 30 , 0f ); DeveloperText.Print("X:" + ( int )GameCamera.Location.X ); GL.Translate( 0f , 24 , 0f ); DeveloperText.Print("Y:" + ( int )GameCamera.Location.Y ); GL.Translate( 0.0f , 24 , 0f ); DeveloperText.Print("Z:" + ( int )GameCamera.Location.Z ); GL.PopMatrix(); QFont.End(); } private void CheckKeyboardInput( FrameEventArgs eventArgs ) { KeyboardState keyboardState = OpenTK.Input.Keyboard.GetState(); GameCamera.ProcessMovement( keyboardState , eventArgs ); if( keyboardState[ Key.Escape ] ) { Exit(); } } protected override void OnUpdateFrame( FrameEventArgs e ) { CheckKeyboardInput( e ); } protected override void OnResize( EventArgs e ) { base.OnResize( e ); GL.Viewport( ClientRectangle.X , ClientRectangle.Y , ClientRectangle.Width , ClientRectangle.Height ); Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView( ( float )Math.PI / 4 , Width / ( float )Height , 0.1f , 1000.0f ); GL.MatrixMode( MatrixMode.Projection ); GL.LoadMatrix( ref projection ); } } } |
有人知道这笔交易吗?因为我很沮丧。
编辑:
我刚刚注意到,如果我使用其他检测方法(SphereInFrustum),一切似乎都可以正常工作。什么都不会被过早裁剪。这是我的新renderloop:
1 2 3 4 5 6 7 8 9 10 | foreach( Voxel voxel in World.Voxels ) { if( voxel.IsVisible() ) { if( GameCamera.Frustum.SphereInFrustum( voxel.Location.X , voxel.Location.Y , voxel.Location.Z , 1.5f ) ) { voxel.Render( GameCamera ); numberOfVoxelsRendered++; } } } |
如您所见,我经过的球体半径为1.5f,并且基本上假装我的所有体素都是球体。它有效,并且没有过早的裁剪,但是我想使用我的VoxelWithinFrustum方法。如果我用该方法做错了什么,谁能发现?还是我使用的参数不正确?
我注意到这篇文章是有关google截锥体的首批Google热门文章之一,因此我想提供适当的有效代码作为新的答案。这是我更新的视锥类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 | public class Frustum { private readonly float[] _clipMatrix = new float[ 16 ]; private readonly float[ , ] _frustum = new float[ 6 , 4 ]; public const int A = 0; public const int B = 1; public const int C = 2; public const int D = 3; public enum ClippingPlane : int { Right = 0 , Left = 1 , Bottom = 2 , Top = 3 , Back = 4 , Front = 5 } private void NormalizePlane( float[ , ] frustum , int side ) { float magnitude = ( float )Math.Sqrt( ( frustum[ side , 0 ] * frustum[ side , 0 ] ) + ( frustum[ side , 1 ] * frustum[ side , 1 ] ) + ( frustum[ side , 2 ] * frustum[ side , 2 ] ) ); frustum[ side , 0 ] /= magnitude; frustum[ side , 1 ] /= magnitude; frustum[ side , 2 ] /= magnitude; frustum[ side , 3 ] /= magnitude; } public bool PointVsFrustum( float x , float y , float z ) { for( int i = 0; i < 6; i++ ) { if( this._frustum[ i , 0 ] * x + this._frustum[ i , 1 ] * y + this._frustum[ i , 2 ] * z + this._frustum[ i , 3 ] <= 0.0f ) { return false; } } return true; } public bool PointVsFrustum( Vector3 location ) { for( int i = 0; i < 6; i++ ) { if( this._frustum[ i , 0 ] * location.X + this._frustum[ i , 1 ] * location.Y + this._frustum[ i , 2 ] * location.Z + this._frustum[ i , 3 ] <= 0.0f ) { return false; } } return true; } public bool SphereVsFrustum( float x , float y , float z , float radius ) { for( int p = 0; p < 6; p++ ) { float d = _frustum[ p , 0 ] * x + _frustum[ p , 1 ] * y + _frustum[ p , 2 ] * z + _frustum[ p , 3 ]; if( d <= -radius ) { return false; } } return true; } public bool SphereVsFrustum( Vector3 location , float radius ) { for( int p = 0; p < 6; p++ ) { float d = _frustum[ p , 0 ] * location.X + _frustum[ p , 1 ] * location.Y + _frustum[ p , 2 ] * location.Z + _frustum[ p , 3 ]; if( d <= -radius ) { return false; } } return true; } public bool VolumeVsFrustum( float x , float y , float z , float width , float height , float length ) { for( int i = 0; i < 6; i++ ) { if( _frustum[ i , A ] * ( x - width ) + _frustum[ i , B ] * ( y - height ) + _frustum[ i , C ] * ( z - length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x + width ) + _frustum[ i , B ] * ( y - height ) + _frustum[ i , C ] * ( z - length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x - width ) + _frustum[ i , B ] * ( y + height ) + _frustum[ i , C ] * ( z - length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x + width ) + _frustum[ i , B ] * ( y + height ) + _frustum[ i , C ] * ( z - length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x - width ) + _frustum[ i , B ] * ( y - height ) + _frustum[ i , C ] * ( z + length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x + width ) + _frustum[ i , B ] * ( y - height ) + _frustum[ i , C ] * ( z + length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x - width ) + _frustum[ i , B ] * ( y + height ) + _frustum[ i , C ] * ( z + length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x + width ) + _frustum[ i , B ] * ( y + height ) + _frustum[ i , C ] * ( z + length ) + _frustum[ i , D ] > 0 ) continue; return false; } return true; } public bool VolumeVsFrustum( BoundingVolume volume ) { for( int i = 0; i < 6; i++ ) { if( _frustum[ i , A ] * ( volume.X - volume.Width ) + _frustum[ i , B ] * ( volume.Y - volume.Height ) + _frustum[ i , C ] * ( volume.Z - volume.Length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( volume.X + volume.Width ) + _frustum[ i , B ] * ( volume.Y - volume.Height ) + _frustum[ i , C ] * ( volume.Z - volume.Length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( volume.X - volume.Width ) + _frustum[ i , B ] * ( volume.Y + volume.Height ) + _frustum[ i , C ] * ( volume.Z - volume.Length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( volume.X + volume.Width ) + _frustum[ i , B ] * ( volume.Y + volume.Height ) + _frustum[ i , C ] * ( volume.Z - volume.Length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( volume.X - volume.Width ) + _frustum[ i , B ] * ( volume.Y - volume.Height ) + _frustum[ i , C ] * ( volume.Z + volume.Length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( volume.X + volume.Width ) + _frustum[ i , B ] * ( volume.Y - volume.Height ) + _frustum[ i , C ] * ( volume.Z + volume.Length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( volume.X - volume.Width ) + _frustum[ i , B ] * ( volume.Y + volume.Height ) + _frustum[ i , C ] * ( volume.Z + volume.Length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( volume.X + volume.Width ) + _frustum[ i , B ] * ( volume.Y + volume.Height ) + _frustum[ i , C ] * ( volume.Z + volume.Length ) + _frustum[ i , D ] > 0 ) continue; return false; } return true; } public bool VolumeVsFrustum( Vector3 location , float width , float height , float length ) { for( int i = 0; i < 6; i++ ) { if( _frustum[ i , A ] * ( location.X - width ) + _frustum[ i , B ] * ( location.Y - height ) + _frustum[ i , C ] * ( location.Z - length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( location.X + width ) + _frustum[ i , B ] * ( location.Y - height ) + _frustum[ i , C ] * ( location.Z - length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( location.X - width ) + _frustum[ i , B ] * ( location.Y + height ) + _frustum[ i , C ] * ( location.Z - length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( location.X + width ) + _frustum[ i , B ] * ( location.Y + height ) + _frustum[ i , C ] * ( location.Z - length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( location.X - width ) + _frustum[ i , B ] * ( location.Y - height ) + _frustum[ i , C ] * ( location.Z + length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( location.X + width ) + _frustum[ i , B ] * ( location.Y - height ) + _frustum[ i , C ] * ( location.Z + length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( location.X - width ) + _frustum[ i , B ] * ( location.Y + height ) + _frustum[ i , C ] * ( location.Z + length ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( location.X + width ) + _frustum[ i , B ] * ( location.Y + height ) + _frustum[ i , C ] * ( location.Z + length ) + _frustum[ i , D ] > 0 ) continue; return false; } return true; } public bool CubeVsFrustum( float x , float y , float z , float size ) { for( int i = 0; i < 6; i++ ) { if( _frustum[ i , A ] * ( x - size ) + _frustum[ i , B ] * ( y - size ) + _frustum[ i , C ] * ( z - size ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x + size ) + _frustum[ i , B ] * ( y - size ) + _frustum[ i , C ] * ( z - size ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x - size ) + _frustum[ i , B ] * ( y + size ) + _frustum[ i , C ] * ( z - size ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x + size ) + _frustum[ i , B ] * ( y + size ) + _frustum[ i , C ] * ( z - size ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x - size ) + _frustum[ i , B ] * ( y - size ) + _frustum[ i , C ] * ( z + size ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x + size ) + _frustum[ i , B ] * ( y - size ) + _frustum[ i , C ] * ( z + size ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x - size ) + _frustum[ i , B ] * ( y + size ) + _frustum[ i , C ] * ( z + size ) + _frustum[ i , D ] > 0 ) continue; if( _frustum[ i , A ] * ( x + size ) + _frustum[ i , B ] * ( y + size ) + _frustum[ i , C ] * ( z + size ) + _frustum[ i , D ] > 0 ) continue; return false; } return true; } public void CalculateFrustum( Matrix4 projectionMatrix , Matrix4 modelViewMatrix ) { _clipMatrix[ 0 ] = ( modelViewMatrix.M11 * projectionMatrix.M11 ) + ( modelViewMatrix.M12 * projectionMatrix.M21 ) + ( modelViewMatrix.M13 * projectionMatrix.M31 ) + ( modelViewMatrix.M14 * projectionMatrix.M41 ); _clipMatrix[ 1 ] = ( modelViewMatrix.M11 * projectionMatrix.M12 ) + ( modelViewMatrix.M12 * projectionMatrix.M22 ) + ( modelViewMatrix.M13 * projectionMatrix.M32 ) + ( modelViewMatrix.M14 * projectionMatrix.M42 ); _clipMatrix[ 2 ] = ( modelViewMatrix.M11 * projectionMatrix.M13 ) + ( modelViewMatrix.M12 * projectionMatrix.M23 ) + ( modelViewMatrix.M13 * projectionMatrix.M33 ) + ( modelViewMatrix.M14 * projectionMatrix.M43 ); _clipMatrix[ 3 ] = ( modelViewMatrix.M11 * projectionMatrix.M14 ) + ( modelViewMatrix.M12 * projectionMatrix.M24 ) + ( modelViewMatrix.M13 * projectionMatrix.M34 ) + ( modelViewMatrix.M14 * projectionMatrix.M44 ); _clipMatrix[ 4 ] = ( modelViewMatrix.M21 * projectionMatrix.M11 ) + ( modelViewMatrix.M22 * projectionMatrix.M21 ) + ( modelViewMatrix.M23 * projectionMatrix.M31 ) + ( modelViewMatrix.M24 * projectionMatrix.M41 ); _clipMatrix[ 5 ] = ( modelViewMatrix.M21 * projectionMatrix.M12 ) + ( modelViewMatrix.M22 * projectionMatrix.M22 ) + ( modelViewMatrix.M23 * projectionMatrix.M32 ) + ( modelViewMatrix.M24 * projectionMatrix.M42 ); _clipMatrix[ 6 ] = ( modelViewMatrix.M21 * projectionMatrix.M13 ) + ( modelViewMatrix.M22 * projectionMatrix.M23 ) + ( modelViewMatrix.M23 * projectionMatrix.M33 ) + ( modelViewMatrix.M24 * projectionMatrix.M43 ); _clipMatrix[ 7 ] = ( modelViewMatrix.M21 * projectionMatrix.M14 ) + ( modelViewMatrix.M22 * projectionMatrix.M24 ) + ( modelViewMatrix.M23 * projectionMatrix.M34 ) + ( modelViewMatrix.M24 * projectionMatrix.M44 ); _clipMatrix[ 8 ] = ( modelViewMatrix.M31 * projectionMatrix.M11 ) + ( modelViewMatrix.M32 * projectionMatrix.M21 ) + ( modelViewMatrix.M33 * projectionMatrix.M31 ) + ( modelViewMatrix.M34 * projectionMatrix.M41 ); _clipMatrix[ 9 ] = ( modelViewMatrix.M31 * projectionMatrix.M12 ) + ( modelViewMatrix.M32 * projectionMatrix.M22 ) + ( modelViewMatrix.M33 * projectionMatrix.M32 ) + ( modelViewMatrix.M34 * projectionMatrix.M42 ); _clipMatrix[ 10 ] = ( modelViewMatrix.M31 * projectionMatrix.M13 ) + ( modelViewMatrix.M32 * projectionMatrix.M23 ) + ( modelViewMatrix.M33 * projectionMatrix.M33 ) + ( modelViewMatrix.M34 * projectionMatrix.M43 ); _clipMatrix[ 11 ] = ( modelViewMatrix.M31 * projectionMatrix.M14 ) + ( modelViewMatrix.M32 * projectionMatrix.M24 ) + ( modelViewMatrix.M33 * projectionMatrix.M34 ) + ( modelViewMatrix.M34 * projectionMatrix.M44 ); _clipMatrix[ 12 ] = ( modelViewMatrix.M41 * projectionMatrix.M11 ) + ( modelViewMatrix.M42 * projectionMatrix.M21 ) + ( modelViewMatrix.M43 * projectionMatrix.M31 ) + ( modelViewMatrix.M44 * projectionMatrix.M41 ); _clipMatrix[ 13 ] = ( modelViewMatrix.M41 * projectionMatrix.M12 ) + ( modelViewMatrix.M42 * projectionMatrix.M22 ) + ( modelViewMatrix.M43 * projectionMatrix.M32 ) + ( modelViewMatrix.M44 * projectionMatrix.M42 ); _clipMatrix[ 14 ] = ( modelViewMatrix.M41 * projectionMatrix.M13 ) + ( modelViewMatrix.M42 * projectionMatrix.M23 ) + ( modelViewMatrix.M43 * projectionMatrix.M33 ) + ( modelViewMatrix.M44 * projectionMatrix.M43 ); _clipMatrix[ 15 ] = ( modelViewMatrix.M41 * projectionMatrix.M14 ) + ( modelViewMatrix.M42 * projectionMatrix.M24 ) + ( modelViewMatrix.M43 * projectionMatrix.M34 ) + ( modelViewMatrix.M44 * projectionMatrix.M44 ); _frustum[ ( int )ClippingPlane.Right , 0 ] = _clipMatrix[ 3 ] - _clipMatrix[ 0 ]; _frustum[ ( int )ClippingPlane.Right , 1 ] = _clipMatrix[ 7 ] - _clipMatrix[ 4 ]; _frustum[ ( int )ClippingPlane.Right , 2 ] = _clipMatrix[ 11 ] - _clipMatrix[ 8 ]; _frustum[ ( int )ClippingPlane.Right , 3 ] = _clipMatrix[ 15 ] - _clipMatrix[ 12 ]; NormalizePlane( _frustum , ( int )ClippingPlane.Right ); _frustum[ ( int )ClippingPlane.Left , 0 ] = _clipMatrix[ 3 ] + _clipMatrix[ 0 ]; _frustum[ ( int )ClippingPlane.Left , 1 ] = _clipMatrix[ 7 ] + _clipMatrix[ 4 ]; _frustum[ ( int )ClippingPlane.Left , 2 ] = _clipMatrix[ 11 ] + _clipMatrix[ 8 ]; _frustum[ ( int )ClippingPlane.Left , 3 ] = _clipMatrix[ 15 ] + _clipMatrix[ 12 ]; NormalizePlane( _frustum , ( int )ClippingPlane.Left ); _frustum[ ( int )ClippingPlane.Bottom , 0 ] = _clipMatrix[ 3 ] + _clipMatrix[ 1 ]; _frustum[ ( int )ClippingPlane.Bottom , 1 ] = _clipMatrix[ 7 ] + _clipMatrix[ 5 ]; _frustum[ ( int )ClippingPlane.Bottom , 2 ] = _clipMatrix[ 11 ] + _clipMatrix[ 9 ]; _frustum[ ( int )ClippingPlane.Bottom , 3 ] = _clipMatrix[ 15 ] + _clipMatrix[ 13 ]; NormalizePlane( _frustum , ( int )ClippingPlane.Bottom ); _frustum[ ( int )ClippingPlane.Top , 0 ] = _clipMatrix[ 3 ] - _clipMatrix[ 1 ]; _frustum[ ( int )ClippingPlane.Top , 1 ] = _clipMatrix[ 7 ] - _clipMatrix[ 5 ]; _frustum[ ( int )ClippingPlane.Top , 2 ] = _clipMatrix[ 11 ] - _clipMatrix[ 9 ]; _frustum[ ( int )ClippingPlane.Top , 3 ] = _clipMatrix[ 15 ] - _clipMatrix[ 13 ]; NormalizePlane( _frustum , ( int )ClippingPlane.Top ); _frustum[ ( int )ClippingPlane.Back , 0 ] = _clipMatrix[ 3 ] - _clipMatrix[ 2 ]; _frustum[ ( int )ClippingPlane.Back , 1 ] = _clipMatrix[ 7 ] - _clipMatrix[ 6 ]; _frustum[ ( int )ClippingPlane.Back , 2 ] = _clipMatrix[ 11 ] - _clipMatrix[ 10 ]; _frustum[ ( int )ClippingPlane.Back , 3 ] = _clipMatrix[ 15 ] - _clipMatrix[ 14 ]; NormalizePlane( _frustum , ( int )ClippingPlane.Back ); _frustum[ ( int )ClippingPlane.Front , 0 ] = _clipMatrix[ 3 ] + _clipMatrix[ 2 ]; _frustum[ ( int )ClippingPlane.Front , 1 ] = _clipMatrix[ 7 ] + _clipMatrix[ 6 ]; _frustum[ ( int )ClippingPlane.Front , 2 ] = _clipMatrix[ 11 ] + _clipMatrix[ 10 ]; _frustum[ ( int )ClippingPlane.Front , 3 ] = _clipMatrix[ 15 ] + _clipMatrix[ 14 ]; NormalizePlane( _frustum , ( int )ClippingPlane.Front ); } } |
我希望有人觉得它有用,而不是像他们那样得到这个死页链接。
可能您设置的高度值(y)错误。我遇到了同样的问题,这有点愚蠢,我在glsl中生成了Y值,对于球体检查来说就像(x,0.0,z)一样。