// public void DrawLine(Vector2 point0, Vector2 point1) // { // var dist = (point1 - point0).Length(); // // // If the distance between the 2 points is less than 2 pixels // // We're exiting // if (dist < 2) // return; // // // Find the middle point between first & second point // Vector2 middlePoint = point0 + (point1 - point0)/2; // // We draw this point on screen // DrawPoint(middlePoint); // // Recursive algorithm launched between first & middle point // // and between middle & second point // DrawLine(point0, middlePoint); // DrawLine(middlePoint, point1); // } // public void DrawBLine(Vector2 point0, Vector2 point1) // { // var x0 = (int)point0.X; // var y0 = (int)point0.Y; // var x1 = (int)point1.X; // var y1 = (int)point1.Y; // // var dx = Math.Abs(x1 - x0); // var dy = Math.Abs(y1 - y0); // var sx = (x0 < x1) ? 1 : -1; // var sy = (y0 < y1) ? 1 : -1; // var err = dx - dy; // // while (true) { // DrawPoint(new Vector2(x0, y0)); // // if ((x0 == x1) && (y0 == y1)) // break; // var e2 = 2 * err; // if (e2 > -dy) { err -= dy; // x0 += sx; // } // if (e2 < dx) { err += dx; // y0 += sy; // } // } // } // drawing line between 2 points from left to right // papb -> pcpd // pa, pb, pc, pd must then be sorted before private void ProcessScanLine(ScanLineData data, Vertex va, Vertex vb, Vertex vc, Vertex vd, Color4 color, Texture texture) { var pa = va.Coordinates; var pb = vb.Coordinates; var pc = vc.Coordinates; var pd = vd.Coordinates; // Thanks to current Y, we can compute the gradient to compute others values like // the starting X (sx) and ending X (ex) to draw between // if pa.Y == pb.Y or pc.Y == pd.Y, gradient is forced to 1 var gradient1 = pa.Y != pb.Y ? (data.CurrentY - pa.Y) / (pb.Y - pa.Y) : 1; var gradient2 = pc.Y != pd.Y ? (data.CurrentY - pc.Y) / (pd.Y - pc.Y) : 1; var sx = (int)Interpolate(pa.X, pb.X, gradient1); var ex = (int)Interpolate(pc.X, pd.X, gradient2); // starting Z & ending Z var z1 = Interpolate(pa.Z, pb.Z, gradient1); var z2 = Interpolate(pc.Z, pd.Z, gradient2); // Interpolating normals on Y var snl = Interpolate(data.Ndotla, data.Ndotlb, gradient1); var enl = Interpolate(data.Ndotlc, data.Ndotld, gradient2); // Interpolating texture coordinates on Y var su = Interpolate(data.Ua, data.Ub, gradient1); var eu = Interpolate(data.Uc, data.Ud, gradient2); var sv = Interpolate(data.Va, data.Vb, gradient1); var ev = Interpolate(data.Vc, data.Vd, gradient2); // drawing a line from left (sx) to right (ex) for (var x = sx; x < ex; x++) { var gradient = (x - sx) / (float)(ex - sx); // Interpolating Z, normal and texture coordinates on X var z = Interpolate(z1, z2, gradient); var ndotl = Interpolate(snl, enl, gradient); // changing the color value using the cosine of the angle // between the light vector and the normal vector // DrawPoint(new Vector3(x, data.currentY, z), color * ndotl); var u = Interpolate(su, eu, gradient); var v = Interpolate(sv, ev, gradient); var textureColor = texture?.Map(u, v) ?? new Color4(1, 1, 1, 1); // changing the native color value using the cosine of the angle // between the light vector and the normal vector // and the texture color DrawPoint(new Vector3(x, data.CurrentY, z), color * ndotl * textureColor); } }
private void DrawTriangle(Vertex v1, Vertex v2, Vertex v3, Color4 color, Texture texture) { // Sorting the points in order to always have this order on screen p1, p2 & p3 // with p1 always up (thus having the Y the lowest possible to be near the top screen) // then p2 between p1 & p3 if (v1.Coordinates.Y > v2.Coordinates.Y) { var temp = v2; v2 = v1; v1 = temp; } if (v2.Coordinates.Y > v3.Coordinates.Y) { var temp = v2; v2 = v3; v3 = temp; } if (v1.Coordinates.Y > v2.Coordinates.Y) { var temp = v2; v2 = v1; v1 = temp; } var p1 = v1.Coordinates; var p2 = v2.Coordinates; var p3 = v3.Coordinates; // Light position var lightPos = new Vector3(0, 10, 10); // computing the cos of the angle between the light vector and the normal vector // it will return a value between 0 and 1 that will be used as the intensity of the color var nl1 = ComputeNDotL(v1.WorldCoordinates, v1.Normal, lightPos); var nl2 = ComputeNDotL(v2.WorldCoordinates, v2.Normal, lightPos); var nl3 = ComputeNDotL(v3.WorldCoordinates, v3.Normal, lightPos); var data = new ScanLineData(); // inverse slopes float dP1P2, dP1P3; // Computing inverse slopes if (p2.Y - p1.Y > 0) { dP1P2 = (p2.X - p1.X) / (p2.Y - p1.Y); } else { dP1P2 = 0; } if (p3.Y - p1.Y > 0) { dP1P3 = (p3.X - p1.X) / (p3.Y - p1.Y); } else { dP1P3 = 0; } // First case where triangles are like that: // P1 // - // -- // - - // - - // - - P2 // - - // - - // - // P3 if (dP1P2 > dP1P3) { // for (var y = (int)p1.Y; y <= (int)p3.Y; y++) // { // if (y < p2.Y) // { // ProcessScanLine(y, p1, p3, p1, p2, color); // } // else // { // ProcessScanLine(y, p1, p3, p2, p3, color); // } // } for (var y = (int)p1.Y; y <= (int)p3.Y; y++) { data.CurrentY = y; if (y < p2.Y) { data.Ndotla = nl1; data.Ndotlb = nl3; data.Ndotlc = nl1; data.Ndotld = nl2; ProcessScanLine(data, v1, v3, v1, v2, color, texture); } else { data.Ndotla = nl1; data.Ndotlb = nl3; data.Ndotlc = nl2; data.Ndotld = nl3; ProcessScanLine(data, v1, v3, v2, v3, color, texture); } } } // First case where triangles are like that: // P1 // - // -- // - - // - - // P2 - - // - - // - - // - // P3 else { // for (var y = (int)p1.Y; y <= (int)p3.Y; y++) // { // if (y < p2.Y) // { // ProcessScanLine(y, p1, p2, p1, p3, color); // } // else // { // ProcessScanLine(y, p2, p3, p1, p3, color); // } // } for (var y = (int)p1.Y; y <= (int)p3.Y; y++) { data.CurrentY = y; if (y < p2.Y) { data.Ndotla = nl1; data.Ndotlb = nl2; data.Ndotlc = nl1; data.Ndotld = nl3; ProcessScanLine(data, v1, v2, v1, v3, color, texture); } else { data.Ndotla = nl2; data.Ndotlb = nl3; data.Ndotlc = nl1; data.Ndotld = nl3; ProcessScanLine(data, v2, v3, v1, v3, color, texture); } } } //if (dP1P2 > dP1P3) //{ // Parallel.For((int)p1.Y, (int)p3.Y + 1, y => // { // if (y < p2.Y) // { // ProcessScanLine(y, p1, p3, p1, p2, color); // } // else // { // ProcessScanLine(y, p1, p3, p2, p3, color); // } // }); //} //else //{ // Parallel.For((int)p1.Y, (int)p3.Y + 1, y => // { // if (y < p2.Y) // { // ProcessScanLine(y, p1, p2, p1, p3, color); // } // else // { // ProcessScanLine(y, p2, p3, p1, p3, color); // } // }); //} }