Browse Source

Added specular and ambient lighting.

angel 7 years ago
parent
commit
e9162882e0
8 changed files with 101 additions and 55 deletions
  1. 3 4
      include/rasterizer.h
  2. 23 10
      include/shader.h
  3. 1 1
      include/softwareRenderer.h
  4. 9 0
      include/vector3D.h
  5. 2 2
      src/camera.cpp
  6. 1 1
      src/engine.cpp
  7. 54 26
      src/rasterizer.cpp
  8. 8 11
      src/softwareRenderer.cpp

+ 3 - 4
include/rasterizer.h

@@ -27,17 +27,16 @@ class Rasterizer{
         //Proper triangle rasterization with vertex interpolation.
         //Proper triangle rasterization with vertex interpolation.
         static void drawTriangles(Vector3f *vertices, IShader &shader, Buffer<Uint32> *pixelBuffer, Buffer<float> *zBuffer);
         static void drawTriangles(Vector3f *vertices, IShader &shader, Buffer<Uint32> *pixelBuffer, Buffer<float> *zBuffer);
 
 
-        //Transforms coordinates from NDC to pixel values(Integers)
+        //Transforms coordinates from NDC to pixel values
         static void viewportTransform(Buffer<Uint32> *pixelBuffer, Vector3f *vertices);
         static void viewportTransform(Buffer<Uint32> *pixelBuffer, Vector3f *vertices);
 
 
-        //Given a set of vertex values, the triangle area in screen space
-        //and a target point returns the barycentric coordinates.
-        static void barycentric(Vector3f &lambdas, float denom, float d00, float d01, float d11, float d20, float d21);
 
 
         static void triBoundBox(int &xMax, int &xMin, int &yMax, int &yMin, Vector3f *vertices, Buffer<Uint32> *pixelBuffer);
         static void triBoundBox(int &xMax, int &xMin, int &yMax, int &yMin, Vector3f *vertices, Buffer<Uint32> *pixelBuffer);
 
 
         static float edge(Vector3f &a, Vector3f &b, Vector3f &c);
         static float edge(Vector3f &a, Vector3f &b, Vector3f &c);
 
 
+        static bool inside(float edge, float a, float b);
+
     private:
     private:
         Rasterizer(){}; //Ensuring an object can never be instanced accidentally
         Rasterizer(){}; //Ensuring an object can never be instanced accidentally
 
 

+ 23 - 10
include/shader.h

@@ -9,8 +9,8 @@
 //Shader Interface for a class that emulates modern GPU fragment and vertex shaders
 //Shader Interface for a class that emulates modern GPU fragment and vertex shaders
 struct IShader {
 struct IShader {
     virtual ~IShader() {};
     virtual ~IShader() {};
-    virtual Vector3f vertex(Vector3f &vertex, Matrix4 &MVP, Vector3f &normals, Vector3f &light, int i) = 0;
-    virtual bool fragment(Vector3f &bari, Vector3f &color, float &depth, Vector3f &zVerts) = 0;
+    virtual Vector3f vertex(Vector3f &vertex, Matrix4 &MVP, Vector3f &normals, Vector3f &light, int i, Vector3f &viewDir) = 0;
+    virtual bool fragment(const Vector3f &bari, Vector3f &color, float &depth, Vector3f &zVerts) = 0;
 };
 };
 
 
 //Simplest shader. Calculates light intensity per triangle.
 //Simplest shader. Calculates light intensity per triangle.
@@ -18,12 +18,12 @@ struct FlatShader : public IShader {
     float varIntensity;
     float varIntensity;
     Vector3f rgb{255,255,255};
     Vector3f rgb{255,255,255};
 
 
-    Vector3f vertex(Vector3f &vertex, Matrix4 &MVP, Vector3f &normals, Vector3f &light, int index) override{
+    Vector3f vertex(Vector3f &vertex, Matrix4 &MVP, Vector3f &normals, Vector3f &light, int index, Vector3f &viewDir) override{
         varIntensity = std::max(0.0f,normals.dotProduct(light));
         varIntensity = std::max(0.0f,normals.dotProduct(light));
         return MVP.matMultVec(vertex); //Transforms verts into projected space
         return MVP.matMultVec(vertex); //Transforms verts into projected space
     }
     }
 
 
-    bool fragment(Vector3f &bari, Vector3f &color, float &depth, Vector3f &zVerts) override{
+    bool fragment(const Vector3f &bari, Vector3f &color, float &depth, Vector3f &zVerts) override{
         depth = bari.dotProduct(zVerts);
         depth = bari.dotProduct(zVerts);
         color = rgb*varIntensity;
         color = rgb*varIntensity;
         return false;
         return false;
@@ -34,17 +34,30 @@ struct FlatShader : public IShader {
 //More Complex shader that calculates a per-vertex intensity and interpolates 
 //More Complex shader that calculates a per-vertex intensity and interpolates 
 //through the fragments of the triangle
 //through the fragments of the triangle
 struct GouraudShader : public IShader {
 struct GouraudShader : public IShader {
-    Vector3f varying_intensity;
+    float ambientStrength = 0.05, diffStrength = 0, specularStrength = 0.5, spec = 0;
+    Vector3f ambient, diffuse, specular;
+    Vector3f lightColor1{1,0,0}, lightColor2{0,0,1}, lightColor3{1,1,1};
+    Vector3f varying_diffuse, varying_specular, reflectDir;
     Vector3f rgb{255,255,255};
     Vector3f rgb{255,255,255};
 
 
-    Vector3f vertex(Vector3f &vertex, Matrix4 &MVP, Vector3f &normals, Vector3f &light, int index) override{
-        varying_intensity.data[index] = std::max(0.0f, normals.dotProduct(light));
+    Vector3f vertex(Vector3f &vertex, Matrix4 &MVP, Vector3f &normal, Vector3f &light, int index, Vector3f &viewDir) override{
+        reflectDir = Vector3f::reflect(-light, normal);
+        varying_specular.data[index] = std::pow( std::max(viewDir.dotProduct(reflectDir), 0.0f), 32.0f);
+        varying_diffuse.data[index] = std::max(0.0f, normal.dotProduct(light));
         return MVP.matMultVec(vertex);
         return MVP.matMultVec(vertex);
     }
     }
 
 
-    bool fragment(Vector3f &bari, Vector3f &color, float &depth, Vector3f &zVerts) override{
-        float intensity = bari.dotProduct(varying_intensity);
-        color = rgb * intensity;
+    bool fragment(const Vector3f &bari, Vector3f &color, float &depth, Vector3f &zVerts) override{
+        ambient = lightColor1 * ambientStrength;
+        
+        diffStrength = bari.dotProduct(varying_diffuse);
+        diffuse = lightColor2 * diffStrength;
+
+        spec = bari.dotProduct(varying_specular);
+        specular = lightColor3 * (specularStrength * spec);
+
+        color = (ambient + diffuse + specular) * rgb;
+
         depth = bari.dotProduct(zVerts);
         depth = bari.dotProduct(zVerts);
         return false;
         return false;
     }
     }

+ 1 - 1
include/softwareRenderer.h

@@ -51,7 +51,7 @@ class SoftwareRenderer {
         void perspectiveDivide(Vector3f *clippedVertices);
         void perspectiveDivide(Vector3f *clippedVertices);
 
 
         //Culling and clipping methods
         //Culling and clipping methods
-        bool backFaceCulling(Vector3f *trianglePrim);
+        bool backFaceCulling(Vector3f *trianglePrim, Vector3f &viewDir);
         bool clipTriangles(Vector3f *clipSpaceVertices);
         bool clipTriangles(Vector3f *clipSpaceVertices);
 
 
         //Pointer to the scene's target camera
         //Pointer to the scene's target camera

+ 9 - 0
include/vector3D.h

@@ -42,6 +42,8 @@ struct Vector3{
     {return Vector3(-x, -y, -z);} 
     {return Vector3(-x, -y, -z);} 
     Vector3 operator*(const T &rhs) const //Scalar-vector multiplication
     Vector3 operator*(const T &rhs) const //Scalar-vector multiplication
     {return Vector3(x*rhs, y*rhs, z*rhs);}
     {return Vector3(x*rhs, y*rhs, z*rhs);}
+    Vector3 operator+(const T &rhs) const //Scalar-vector multiplication
+    {return Vector3(x+rhs, y+rhs, z+rhs);}
 
 
     //Vector-vector operations
     //Vector-vector operations
     Vector3 operator-(const Vector3 &rhs) const
     Vector3 operator-(const Vector3 &rhs) const
@@ -50,6 +52,9 @@ struct Vector3{
     Vector3 operator+(const Vector3 &rhs) const
     Vector3 operator+(const Vector3 &rhs) const
     {return Vector3(x + rhs.x, y + rhs.y, z + rhs.z);}
     {return Vector3(x + rhs.x, y + rhs.y, z + rhs.z);}
 
 
+    Vector3 operator*(const Vector3 &rhs) const
+    {return Vector3(x * rhs.x, y * rhs.y, z * rhs.z);}
+
     Vector3 crossProduct(const Vector3 &r) const
     Vector3 crossProduct(const Vector3 &r) const
     {return Vector3( (y*r.z - z*r.y), (z*r.x - x*r.z), (x*r.y - y*r.x) );}
     {return Vector3( (y*r.z - z*r.y), (z*r.x - x*r.z), (x*r.y - y*r.x) );}
 
 
@@ -77,6 +82,10 @@ struct Vector3{
 
 
         return *this;
         return *this;
     }
     }
+
+    static Vector3 reflect(const Vector3 &I, const Vector3 &N){
+        return I - ((N * I.dotProduct(N)) * 2.0f);
+    }
     
     
     //Print for debugging purposes
     //Print for debugging purposes
     void print(){
     void print(){

+ 2 - 2
src/camera.cpp

@@ -12,8 +12,8 @@ Camera::Camera(){
 void Camera::update(){
 void Camera::update(){
     float t = static_cast<float>(SDL_GetTicks());
     float t = static_cast<float>(SDL_GetTicks());
     float radius = 12;
     float radius = 12;
-    float camX   = std::sin(t/20000) * radius;
-    float camZ   = std::cos(t/20000) * radius;
+    float camX   = std::sin(t/4000) * radius;
+    float camZ   = std::cos(t/4000) * radius;
     position.x   = camX;
     position.x   = camX;
     position.y   = 0;
     position.y   = 0;
     position.z   = camZ;
     position.z   = camZ;

+ 1 - 1
src/engine.cpp

@@ -94,5 +94,5 @@ void Engine::run(){
         printf("%2.1d: Loop elapsed time (ms):%d\n",count,end - start);
         printf("%2.1d: Loop elapsed time (ms):%d\n",count,end - start);
         //break;
         //break;
     }
     }
-    printf("Closing engine\n");
+    printf("Closing down engine:\n");
 }
 }

+ 54 - 26
src/rasterizer.cpp

@@ -81,41 +81,68 @@ void Rasterizer::drawWireFrame(Vector3f *vertices, IShader &shader, Buffer<Uint3
 //Draws triangles using baricentric coordinates,
 //Draws triangles using baricentric coordinates,
 void Rasterizer::drawTriangles(Vector3f *vertices, IShader &shader, Buffer<Uint32> *pixelBuffer, Buffer<float> *zBuffer){
 void Rasterizer::drawTriangles(Vector3f *vertices, IShader &shader, Buffer<Uint32> *pixelBuffer, Buffer<float> *zBuffer){
 
 
+    //Transform into viewport coordinates 
     Rasterizer::viewportTransform(pixelBuffer, vertices);
     Rasterizer::viewportTransform(pixelBuffer, vertices);
+
     //Finding triangle bounding box limits & clips it to the screen width and height
     //Finding triangle bounding box limits & clips it to the screen width and height
     int xMax, xMin, yMax, yMin;
     int xMax, xMin, yMax, yMin;
     Rasterizer::triBoundBox(xMax, xMin, yMax, yMin, vertices, pixelBuffer);
     Rasterizer::triBoundBox(xMax, xMin, yMax, yMin, vertices, pixelBuffer);
 
 
     //Per fragment variables            
     //Per fragment variables            
     float depth, area;
     float depth, area;
-    Vector3f bari, rgbVals, point;
+    Vector3f e, e_row, rgbVals{255,255,255};
+
+    //Per triangle variables
     Vector3f zVals{vertices[0].z,vertices[1].z,vertices[2].z};
     Vector3f zVals{vertices[0].z,vertices[1].z,vertices[2].z};
     area = 1/edge(vertices[0],vertices[1],vertices[2]);
     area = 1/edge(vertices[0],vertices[1],vertices[2]);
+    float A01 = vertices[0].y - vertices[1].y, B01= vertices[1].x - vertices[0].x;
+    float A12 = vertices[1].y - vertices[2].y, B12= vertices[2].x - vertices[1].x;
+    float A20 = vertices[2].y - vertices[0].y, B20= vertices[0].x - vertices[2].x;
+
+    //Bary coordinates at 
+    Vector3f point{(float)xMin, (float)yMin, 0};
+    e_row.x = edge(vertices[1], vertices[2], point);
+    e_row.y = edge(vertices[2], vertices[0], point);
+    e_row.z = edge(vertices[0], vertices[1], point);
+
 
 
     //Iterating through each pixel in triangle bounding box
     //Iterating through each pixel in triangle bounding box
     for(point.y = yMin; point.y <= yMax; ++point.y){
     for(point.y = yMin; point.y <= yMax; ++point.y){
+        //Bary coordinates at start of row
+        e.x = e_row.x;
+        e.y = e_row.y;
+        e.z = e_row.z;
+
         for(point.x = xMin; point.x <= xMax; ++point.x){
         for(point.x = xMin; point.x <= xMax; ++point.x){
 
 
-            bari.x = edge(vertices[1], vertices[2], point);
-            bari.y = edge(vertices[2], vertices[0], point);
-            bari.z = edge(vertices[0], vertices[1], point);
+            //Only draw if inside pixel and following top left rule
+            if(inside(e.x, A01, B01) && inside(e.y, A12, B12) && inside(e.z, A20, B20) ){
 
 
-            //Only draw if inside pixel
-            if(bari.x >= 0 && bari.y >= 0 && bari.z >= 0 ){
                 //Run fragment shader
                 //Run fragment shader
-                bari = bari* area;
-                shader.fragment(bari, rgbVals, depth, zVals);
-
+                shader.fragment((e*area), rgbVals, depth, zVals);
                 //Zbuffer check
                 //Zbuffer check
                 if((*zBuffer)(point.x,point.y) < depth){
                 if((*zBuffer)(point.x,point.y) < depth){
                     if(depth <= 1.0){
                     if(depth <= 1.0){
                         (*zBuffer)(point.x,point.y) = depth;
                         (*zBuffer)(point.x,point.y) = depth;
                         (*pixelBuffer)(point.x,point.y) = SDL_MapRGBA(mappingFormat,
                         (*pixelBuffer)(point.x,point.y) = SDL_MapRGBA(mappingFormat,
-                        rgbVals.data[0],rgbVals.data[1],rgbVals.data[2],0xFF);
+                        std::min(rgbVals.data[0],255.0f),
+                        std::min(rgbVals.data[1],255.0f),
+                        std::min(rgbVals.data[2],255.0f),
+                        0xFF);
                     }   
                     }   
                 }
                 }
             }
             }
+
+            //One step to the right
+            e.x += A12;
+            e.y += A20;
+            e.z += A01;      
         }
         }
+
+        //One row step
+        e_row.x += B12;
+        e_row.y += B20;
+        e_row.z += B01;
     }
     }
 }  
 }  
 
 
@@ -124,27 +151,20 @@ void Rasterizer::viewportTransform(Buffer<Uint32> *pixelBuffer, Vector3f *vertic
         vertices[i].x = ((vertices[i].x + 1 ) * pixelBuffer->mWidth * 0.5);
         vertices[i].x = ((vertices[i].x + 1 ) * pixelBuffer->mWidth * 0.5);
         vertices[i].y = ((vertices[i].y + 1 ) * pixelBuffer->mHeight * 0.5);
         vertices[i].y = ((vertices[i].y + 1 ) * pixelBuffer->mHeight * 0.5);
     }
     }
-    //Swap because inverting y changes winding order
-
 }
 }
 
 
-//Calculates baricentric coordinates of triangles using the cross product
-void Rasterizer::barycentric(Vector3f &lambdas, float denom, float d00, float d01, float d11, float d20, float d21){
-    lambdas.data[1] = denom  * ((d11*d20) - (d01*d21)); 
-    lambdas.data[2] = denom  * ((d00*d21) - (d01*d20));
-    lambdas.data[0] = 1.0f - lambdas.data[1] - lambdas.data[2];
-}
+
 
 
 void Rasterizer::triBoundBox(int &xMax, int &xMin, int &yMax, int &yMin,Vector3f *vertices, Buffer<Uint32> *pixelBuffer){
 void Rasterizer::triBoundBox(int &xMax, int &xMin, int &yMax, int &yMin,Vector3f *vertices, Buffer<Uint32> *pixelBuffer){
-    // xMax = std::ceil(std::max({vertices[0].x, vertices[1].x, vertices[2].x,}));
-    // xMin = std::ceil(std::min({vertices[0].x, vertices[1].x, vertices[2].x,}));
-    xMax = std::max({vertices[0].x, vertices[1].x, vertices[2].x});
-    xMin = std::min({vertices[0].x, vertices[1].x, vertices[2].x});
+    xMax = std::ceil(std::max({vertices[0].x, vertices[1].x, vertices[2].x,}));
+    xMin = std::ceil(std::min({vertices[0].x, vertices[1].x, vertices[2].x,}));
+    //xMax = std::max({vertices[0].x, vertices[1].x, vertices[2].x});
+    //xMin = std::min({vertices[0].x, vertices[1].x, vertices[2].x});
 
 
-    // yMax = std::ceil(std::max({vertices[0].y, vertices[1].y, vertices[2].y,}));
-    // yMin = std::ceil(std::min({vertices[0].y, vertices[1].y, vertices[2].y,}));
-    yMax = std::max({vertices[0].y, vertices[1].y, vertices[2].y});
-    yMin = std::min({vertices[0].y, vertices[1].y, vertices[2].y});
+    yMax = std::ceil(std::max({vertices[0].y, vertices[1].y, vertices[2].y,}));
+    yMin = std::ceil(std::min({vertices[0].y, vertices[1].y, vertices[2].y,}));
+    //yMax = std::max({vertices[0].y, vertices[1].y, vertices[2].y});
+    //yMin = std::min({vertices[0].y, vertices[1].y, vertices[2].y});
 
 
     //Clip against screen
     //Clip against screen
     xMax = std::min(xMax, pixelBuffer->mWidth -1);
     xMax = std::min(xMax, pixelBuffer->mWidth -1);
@@ -159,4 +179,12 @@ float Rasterizer::edge(Vector3f &a, Vector3f &b, Vector3f &c){
     return (b.x - a.x)*(c.y-a.y) - (b.y - a.y)*(c.x - a.x);
     return (b.x - a.x)*(c.y-a.y) - (b.y - a.y)*(c.x - a.x);
 }
 }
 
 
+bool Rasterizer::inside(float edge, float a, float b){
+    if (edge > 0) return true;
+    if (edge < 0) return false;
+    if ( a > 0 )  return true;
+    if ( a < 0 )  return false;
+    if ( b > 0 )  return true;
+    return false;
+}
 
 

+ 8 - 11
src/softwareRenderer.cpp

@@ -37,11 +37,8 @@ void SoftwareRenderer::drawTriangularMesh(Mesh* triMesh){
     GouraudShader shader;
     GouraudShader shader;
 
 
     //Basic light direction
     //Basic light direction
-    float t = static_cast<float>(SDL_GetTicks());
-    float radius = 12;
-    float lightX   = std::sin(t/2000) * radius;
-    float lightZ   = std::cos(t/2000) * radius;
-    Vector3f lightDir{-lightX, 0, -lightZ};
+    Vector3f lightDir{1, 0, 1};
+    Vector3f viewDir;
 
 
     //Building ModelViewProjection matrix
     //Building ModelViewProjection matrix
     Matrix4 MVP = (mCamera->projectionMatrix)*(mCamera->viewMatrix);
     Matrix4 MVP = (mCamera->projectionMatrix)*(mCamera->viewMatrix);
@@ -57,12 +54,12 @@ void SoftwareRenderer::drawTriangularMesh(Mesh* triMesh){
         buildTri(n,normalPrim, *normals);
         buildTri(n,normalPrim, *normals);
 
 
         //Skip faces that are pointing away from us
         //Skip faces that are pointing away from us
-       if (backFaceCulling(trianglePrimitive)) continue;
+       if (backFaceCulling(trianglePrimitive, viewDir)) continue;
 
 
         //Apply vertex shader
         //Apply vertex shader
         for(int i = 0; i < 3; ++i){
         for(int i = 0; i < 3; ++i){
             //Vector3 normal = (mCamera->viewMatrix).matMultVec(normalPrim[i]);
             //Vector3 normal = (mCamera->viewMatrix).matMultVec(normalPrim[i]);
-            trianglePrimitive[i] = shader.vertex(trianglePrimitive[i], MVP, normalPrim[i], lightDir.normalized(), i);
+            trianglePrimitive[i] = shader.vertex(trianglePrimitive[i], MVP, normalPrim[i], lightDir.normalized(), i, viewDir);
         }
         }
 
 
         //Skip triangles that are outside viewing frustrum
         //Skip triangles that are outside viewing frustrum
@@ -115,18 +112,18 @@ void SoftwareRenderer::buildTri(Vector3i &index, Vector3f *primitive, std::vecto
     }
     }
 }
 }
 
 
-bool SoftwareRenderer::backFaceCulling(Vector3f *trianglePrim){
+bool SoftwareRenderer::backFaceCulling(Vector3f *trianglePrim, Vector3f &viewDir){
         //Triangle surface normal 
         //Triangle surface normal 
         //Should probably be calculated on load next time
         //Should probably be calculated on load next time
         Vector3f N1 = trianglePrim[1] - trianglePrim[0];
         Vector3f N1 = trianglePrim[1] - trianglePrim[0];
         Vector3f N2 = trianglePrim[2] - trianglePrim[0];
         Vector3f N2 = trianglePrim[2] - trianglePrim[0];
         Vector3f N  = (N1.crossProduct(N2)).normalized();
         Vector3f N  = (N1.crossProduct(N2)).normalized();
 
 
-        Vector3f view_dir =  mCamera->position -  trianglePrim[0];
-        view_dir.normalized();
+        viewDir =  mCamera->position -  trianglePrim[0];
+        viewDir.normalized();
 
 
         //Returns false if the triangle cannot see the camera
         //Returns false if the triangle cannot see the camera
-        float intensity =  N.dotProduct(view_dir);
+        float intensity =  N.dotProduct(viewDir);
         return intensity <= 0.0;
         return intensity <= 0.0;
 }
 }