Browse Source

Added Frustrum Culling.

angel 7 years ago
parent
commit
32bc40212f

+ 5 - 7
include/camera.h

@@ -3,10 +3,13 @@
 
 #include "matrix.h"
 #include "vector3D.h"
-
+#include "geometry.h"
+#include "displayManager.h"
 
 struct Camera{
     Camera();
+    
+    bool checkVisibility(AABox *bounds);
 
     //In the future user input should control this. For now just simple movement
     void update();
@@ -19,12 +22,7 @@ struct Camera{
     Vector3f target{0,0,0};
     Vector3f up{0,1,0};
 
-    //Variables that determine frustrum (Future class?)
-    //Used to build projection matrix
-    float fov{90};
-    float near{0.1};
-    float far{100};
-    float aspectRatio{1.0};
+    Frustrum cameraFrustrum{DisplayManager::SCREEN_ASPECT_RATIO};
 };
 
 #endif

+ 47 - 0
include/geometry.h

@@ -0,0 +1,47 @@
+#ifndef GEOMETRY_H
+#define GEOMETRY_H
+
+#include "vector3D.h"
+#include "mesh.h"
+#include "matrix.h"
+
+//Struct containing vertex data for a AABB
+//around the model. Primarily for use in frustum culling
+//Regiong R = {(x, y, z) | min.x <=x <= max.x | same Y | same Z}
+struct AABox{
+    Vector3f minPoints;
+    Vector3f maxPoints;
+
+    //Builds axis aligned bounding box of the given mesh
+    void buildAABB(const Mesh &mesh);
+};
+
+struct Plane{
+    Vector3f normal;
+    float D;
+
+    float distance(const Vector3f &points);
+    void setNormalAndPoint(const Vector3f &normal, const Vector3f &point);
+};
+
+class Frustrum{
+    private:
+    	enum {
+		TOP = 0, BOTTOM, LEFT,
+		RIGHT, NEARP, FARP
+        };
+    public:
+        float  fov, AR, near, far , nearH, nearW, farH, farW;
+        Frustrum(float ratio): fov(90), near(0.1), far(10000), AR(ratio){};
+        Plane pl[6];
+
+        void setCamInternals();
+        void updatePlanes(Matrix4 &viewMat, const Vector3f &cameraPos) ;
+        bool checkIfInside(AABox *bounds);
+};
+
+
+
+
+
+#endif

+ 0 - 12
include/mesh.h

@@ -20,16 +20,4 @@ struct Mesh{
     void describeMesh();
 };
 
-//Struct containing vertex data for a tight bounding box around a model.
-//Primarily for use in frustum culling
-struct Bound3{
-    float mMinX = 0.0;
-    float mMinY = 0.0;
-    float mMinZ = 0.0;
-
-    float mMaxX = 0.0;
-    float mMaxY = 0.0;
-    float mMaxZ = 0.0;
-};
-
 #endif

+ 4 - 1
include/model.h

@@ -3,6 +3,7 @@
 
 #include <string>
 #include "mesh.h"
+#include "geometry.h"
 #include "matrix.h"
 #include "objParser.h"
 
@@ -12,6 +13,8 @@ class Model{
 
         Mesh *getMesh();
 
+        AABox *getBounds();
+
         void update();
 
         //Prints the mesh vertices for debugging
@@ -23,7 +26,7 @@ class Model{
         //Calculates the boundary box of the mesh in object space
         void buildBoundaryBox();
         Mesh mMesh;
-        Bound3 mBounds;
+        AABox mBounds;
 };
 
 #endif

+ 17 - 9
include/softwareRenderer.h

@@ -17,15 +17,23 @@ class SoftwareRenderer {
         bool startUp(int w, int h);  
         void shutDown();      
 
-        //Draws mesh assuming it is made of triangular primites
-        //1.-Gets pointers to render data form mesh
-        //2.-Builds MVP 
-        //3.-Iterates through all triangle faces
-        //4.-Runs backface culling algo
-        //5.-Applies vertex shader per vertex
-        //6.-Performs clipping of triangles outside frustrum
-        //7.-Runs perspective divide
-        //8.-
+        //Overview:
+        //01.-Gets pointers to render data form mesh
+        //02.-Builds MVP 
+        //03.-Iterates through all triangle faces
+        //04.-Runs backface culling algo
+        //05.-Applies vertex shader per vertex
+        //06.-Performs clipping of triangles outside frustrum
+        //07.-Applies perspective divide
+        //08.-Sends to triangle rasterizer
+        //09.-NDC -> viewport transform
+        //10.-Iterates through triangle bounding box 
+        //11.-Calculates barycentric coordinates
+        //12.-Culls pixels outside of triangle or screen
+        //13.-Runs fragment shader
+        //14.-zBuffer update
+        //15.-Writes to frame buffer
+        //16.-Swap buffer
         void drawTriangularMesh(Mesh * triMesh);
 
         void clearBuffers();

+ 2 - 6
include/vector3D.h

@@ -38,12 +38,8 @@ struct Vector3{
     }
 
     //Scalar-vector operations
-    Vector3 &operator-(){//Negate components of vector
-        x = -x;
-        y = -y;
-        z = -z;
-        return *this;
-    }; 
+    Vector3 operator-()
+    {return Vector3(-x, -y, -z);} 
     Vector3 operator*(const T &rhs) const //Scalar-vector multiplication
     {return Vector3(x*rhs, y*rhs, z*rhs);}
 

BIN
models/.teapot.obj.swp


+ 14 - 7
src/camera.cpp

@@ -1,20 +1,27 @@
 #include "camera.h"
 #include "SDL.h"
-#include "displayManager.h"
+
 
 Camera::Camera(){
     viewMatrix = Matrix4::lookAt(position,target,up);
-    aspectRatio =  DisplayManager::SCREEN_ASPECT_RATIO;
-    projectionMatrix = Matrix4::projectionMatrix(fov, aspectRatio, near,far);
+    projectionMatrix = Matrix4::projectionMatrix(cameraFrustrum.fov, cameraFrustrum.AR, cameraFrustrum.near, cameraFrustrum.far);
+    cameraFrustrum.setCamInternals();
+    cameraFrustrum.updatePlanes(viewMatrix, position);
 }
 
 void Camera::update(){
     float t = static_cast<float>(SDL_GetTicks());
-    float radius = 5;
+    float radius = 15;
     float camX   = std::sin(t/4000) * radius;
     float camZ   = std::cos(t/4000) * radius;
-    position.x   = 0;
-    position.y   = 0;
-    position.z   = camZ;
+    position.x   = camX;
+    position.y   = camX;
+    position.z   = 5;
+    target.z     = -1000;
     viewMatrix   = Matrix4::lookAt(position,target,up);
+    cameraFrustrum.updatePlanes(viewMatrix, position);
+}
+
+bool Camera::checkVisibility(AABox *bounds){
+    return cameraFrustrum.checkIfInside(bounds);
 }

+ 116 - 0
src/geometry.cpp

@@ -0,0 +1,116 @@
+#include "geometry.h"
+#include <limits>
+#include <math.h>
+
+//-------------------------------AABOX------------------------------------//
+
+void AABox::buildAABB(const Mesh &mesh){
+    Vector3f minVals(std::numeric_limits<float>::max());
+    Vector3f maxVals(std::numeric_limits<float>::min());
+
+    //Iterating through every vertx in the mesh
+    for(int i = 0; i < mesh.numVertices; ++i){
+        //Checking the current vertex for all axes
+        for(int ax = 0; ax < 3; ++ax){
+            //Setting max values
+            if(mesh.vertices[i].data[ax] > maxVals.data[ax]){
+                maxVals.data[ax] = mesh.vertices[i].data[ax];
+            }
+            //Setting min values
+            if(mesh.vertices[i].data[ax] < minVals.data[ax]){
+                minVals.data[ax] = mesh.vertices[i].data[ax];
+            }
+        }
+    }
+    minPoints = minVals;
+    maxPoints = maxVals;
+}
+
+//---------------------------------PLANE------------------------------------//
+
+float Plane::distance(const Vector3f &points){
+    return normal.dotProduct(points) + D;
+}
+
+void Plane::setNormalAndPoint(const Vector3f &n, const Vector3f &p0){
+    normal = n;
+    D = -(n.dotProduct(p0));
+}
+
+//-------------------------------FRUSTRUM------------------------------------//
+
+void Frustrum::setCamInternals(){
+    float tanHalfFOV  =  tan( (fov/2) * (M_PI / 180) );
+    nearH = near * tanHalfFOV;
+    nearW = nearH * AR;
+
+    farH  = far * tanHalfFOV;
+    farW  = farH * AR;
+}
+
+void Frustrum::updatePlanes(Matrix4 &viewMat, const Vector3f &cameraPos){
+    Vector3f X(viewMat(0,0), viewMat(0,1), viewMat(0,2));
+    Vector3f Y(viewMat(1,0), viewMat(1,1), viewMat(1,2));
+    Vector3f Z(viewMat(2,0), viewMat(2,1), viewMat(2,2));
+    
+    Vector3f nearCenter = cameraPos - Z * near;
+    Vector3f farCenter  = cameraPos - Z * far;
+
+    Vector3f point;
+    Vector3f normal;
+
+    //Near plane 
+    pl[NEARP].setNormalAndPoint(-Z, nearCenter);
+    //Far plane 
+    pl[FARP].setNormalAndPoint(Z, farCenter);
+
+    //Top plane
+    point  = nearCenter + Y*nearH;
+    normal = (point - cameraPos).normalized();
+    normal = normal.crossProduct(X);
+    pl[TOP].setNormalAndPoint(normal, point);
+
+    //Bottom plane
+    point  = nearCenter - Y*nearH;
+    normal = (point - cameraPos).normalized();
+    normal = X.crossProduct(normal);
+    pl[BOTTOM].setNormalAndPoint(normal, point);
+
+    //Left plane
+    point  = nearCenter - X*nearW;
+    normal = (point - cameraPos).normalized();
+    normal = normal.crossProduct(Y);
+    pl[LEFT].setNormalAndPoint(normal, point);
+
+    //Right plane
+    point  = nearCenter + X*nearW;
+    normal = (point - cameraPos).normalized();
+    normal = Y.crossProduct(normal);
+    pl[RIGHT].setNormalAndPoint(normal, point);
+    
+}
+
+//False is fully outside, true if inside or intersects
+//based on iquilez method
+bool Frustrum::checkIfInside(AABox *box){
+
+    //Check box outside or inside of frustrum
+    for(int i =0; i < 6; ++i){
+        int out = 0;
+        out += ((pl[i].distance(Vector3f(box->minPoints.x, box->minPoints.y, box->minPoints.z))< 0.0 )?1:0);
+        out += ((pl[i].distance(Vector3f(box->maxPoints.x, box->minPoints.y, box->minPoints.z))< 0.0 )?1:0);
+        out += ((pl[i].distance(Vector3f(box->minPoints.x, box->maxPoints.y, box->minPoints.z))< 0.0 )?1:0);
+        out += ((pl[i].distance(Vector3f(box->maxPoints.x, box->maxPoints.y, box->minPoints.z))< 0.0 )?1:0);
+        out += ((pl[i].distance(Vector3f(box->minPoints.x, box->minPoints.y, box->maxPoints.z))< 0.0 )?1:0);
+        out += ((pl[i].distance(Vector3f(box->maxPoints.x, box->minPoints.y, box->maxPoints.z))< 0.0 )?1:0);
+        out += ((pl[i].distance(Vector3f(box->minPoints.x, box->maxPoints.y, box->maxPoints.z))< 0.0 )?1:0);
+        out += ((pl[i].distance(Vector3f(box->maxPoints.x, box->maxPoints.y, box->maxPoints.z))< 0.0 )?1:0);
+        
+        if (out == 8) return false;
+    }
+    return true;
+}
+
+
+
+

+ 1 - 8
src/matrix.cpp

@@ -23,14 +23,7 @@ Vector3f Matrix4::matMultVec(Vector3f &vec){
                vec.y*(*this)(3,1)+
                vec.z*(*this)(3,2)+
                (*this)(3,3); //Assuming wO of vec always  = 1
-    
-    //Division is expensive, only do it if you need it
-    //Also here is where perspective divide happens!
-    // if(w2 != 1){
-    //     newVec.x /= w2;
-    //     newVec.y /= w2;
-    //     newVec.z /= w2;
-    // }
+
     newVec.w = w2;
 
     return newVec;

+ 2 - 1
src/mesh.cpp

@@ -5,4 +5,5 @@ void Mesh::describeMesh(){
         printf("Vertex  %2.1d: %f, %f, %f \n",i,vertices[i].x, vertices[i].y, vertices[i].z);
     }
     printf("Meshsize is: %d \n", numVertices);
-}
+}
+    

+ 8 - 60
src/model.cpp

@@ -1,11 +1,10 @@
 #include "model.h"
 #include "vector3D.h"
-#include <limits>
 
 Model::Model(std::string path, TransformParameters &initParameters){
     OBJ::buildMeshFromFile(mMesh, path);
     initPosition(initParameters);
-    buildBoundaryBox();
+    mBounds.buildAABB(mMesh);
 }
 
 Mesh * Model::getMesh(){
@@ -22,64 +21,13 @@ void Model::initPosition(TransformParameters initVals){
         (*vertices)[i] = modelMatrix.matMultVec((*vertices)[i]);
     }
 }
-
-//I know this is incredibly inefficient, I only need to do it once for now
-//If if I ever need something faster I will move this into the vertex loading
-//function and make use of the read there.
-void Model::buildBoundaryBox(){
-    float minX = std::numeric_limits<float>::max();
-    float maxX = std::numeric_limits<float>::min();
-
-    float minY = minX;
-    float maxY = maxX;
-
-    float minZ = minX;
-    float maxZ = maxX;
-
-    for(int i = 0; i < mMesh.numVertices; ++i){
-        float x = mMesh.vertices[i].x;
-        float y = mMesh.vertices[i].y;
-        float z = mMesh.vertices[i].z;
-
-        if(x > maxX){
-            maxX = x;
-        }
-        else{
-            if(x < minX){
-                minX = x;
-            }
-        }
-
-        if(y > maxY){
-            maxY = y;
-        }
-        else{
-            if(y < minY){
-                minY = y;
-            }
-        }
-
-        if(z > maxZ){
-            maxZ = z;
-        }
-        else{
-            if(z < minZ){
-                minZ = z;
-            }
-        }
-    }
-
-    mBounds.mMaxX = maxX;
-    mBounds.mMaxY = maxY;
-    mBounds.mMaxZ = maxZ;
-
-    mBounds.mMinX = minX;
-    mBounds.mMinY = minY;
-    mBounds.mMinZ = minZ;
-
-}
-
 //TO DO 
 void Model::update(){
-    
+    //You'd get physics updates or user input updates or whatever here
+    //Move
+    //Update AABB
+}
+
+AABox *Model::getBounds(){
+    return &mBounds;
 }

+ 1 - 1
src/rasterizer.cpp

@@ -141,7 +141,7 @@ void Rasterizer::viewportTransform(Buffer<Uint32> *pixelBuffer, Vector3f *vertic
     }
 }
 
-//Calculates baricentric coordinates of triangles using the cross
+//Calculates baricentric coordinates of triangles using the cross product
 void Rasterizer::barycentric(Vector3f &lambdas, float invArea, int x, int y,  
                         std::array<int, 3>   &xVerts, std::array<int, 3>   &yVerts){
     for(int i = 0; i < 3; ++i){

+ 2 - 15
src/scene.cpp

@@ -45,7 +45,7 @@ void Scene::frustrumCulling(){
     bool visible = true;
     for(Model *model : modelsInScene){
 
-        //TO DO Visibility check against camera's frustrum planes
+        visible = mainCamera.checkVisibility(model->getBounds());
 
         if (visible){
             visibleModels.push_back(model);
@@ -63,17 +63,4 @@ Camera* Scene::getCurrentCamera(){
 
 bool Scene::checkIfEmpty(){
     return emptyScene;
-}
-
-// //If the frustrum culling returns true it means the model has been culled
-    // //Because no other models are in the scene we return
-    // // if (frustrumCulling(models, viewMatrix)){
-    // //     printf("Model culled!\n");
-    // //     return;
-    // // } 
-
-
-//     //stupid frustrum culling
-    //     //if(v1.x < -1 || v1.x > 1 || v1.y < -1 || v1.y > 1 || v1.z > 1 || v1.z < -1) continue;
-    //     //if(v2.x < -1 || v2.x > 1 || v2.y < -1 || v2.y > 1 || v2.z > 1 || v2.z < -1) continue;
-    //     //if(v3.x < -1 || v3.x > 1 || v3.y < -1 || v3.y > 1 || v3.z > 1 || v3.z < -1) continue;
+}

+ 1 - 1
src/softwareRenderer.cpp

@@ -33,7 +33,7 @@ void SoftwareRenderer::drawTriangularMesh(Mesh* triMesh){
     Vector3f trianglePrimitive[3];
     Vector3f normalPrim[3];
 
-    //Initializing shader
+    //Initializing shader 
     GouraudShader shader;
 
     //Basic light direction