public void DrawTriangle(ref Vertex v0, ref Vertex v1, ref Vertex v2, Color4 color) { // 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 (v0.Coordinates.Y > v1.Coordinates.Y) MathExtensions.Swap(ref v0, ref v1); if (v1.Coordinates.Y > v2.Coordinates.Y) MathExtensions.Swap(ref v1, ref v2); if (v0.Coordinates.Y > v1.Coordinates.Y) MathExtensions.Swap(ref v0, ref v1); Vector3 p0 = v0.Coordinates; Vector3 p1 = v1.Coordinates; Vector3 p2 = v2.Coordinates; // 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 nl0 = MathExtensions.ComputeNDotL(ref v0.WorldCoordinates, ref v0.Normal, ref LightPosition); var nl1 = MathExtensions.ComputeNDotL(ref v1.WorldCoordinates, ref v1.Normal, ref LightPosition); var nl2 = MathExtensions.ComputeNDotL(ref v2.WorldCoordinates, ref v2.Normal, ref LightPosition); // computing lines' directions // http://en.wikipedia.org/wiki/Slope // Computing slopes var d1 = (p1.Y - p0.Y > 0) ? (p1.X - p0.X) / (p1.Y - p0.Y) : 0; var d2 = (p2.Y - p0.Y > 0) ? (p2.X - p0.X) / (p2.Y - p0.Y) : 0; var data = new ScanlineData(); // First case where triangles are like that: // P1 // - // -- // - - // - - // - - P2 // - - // - - // - // P3 if (d1 > d2) { for (var y = (int)p0.Y; y <= (int)p2.Y; y++) { data.Y = y; if (y < p1.Y) { data.NDotL0 = nl0; data.NDotL1 = nl2; data.NDotL2 = nl0; data.NDotL3 = nl1; ProcessScanline(ref data, ref v0, ref v2, ref v0, ref v1, ref color); } else { data.NDotL0 = nl0; data.NDotL1 = nl2; data.NDotL2 = nl1; data.NDotL3 = nl2; ProcessScanline(ref data, ref v0, ref v2, ref v1, ref v2, ref color); } } } // First case where triangles are like that: // P1 // - // -- // - - // - - // P2 - - // - - // - - // - // P3 else { for (var y = (int)p0.Y; y <= (int)p2.Y; y++) { data.Y = y; if (y < p1.Y) { data.NDotL0 = nl0; data.NDotL1 = nl1; data.NDotL2 = nl0; data.NDotL3 = nl2; ProcessScanline(ref data, ref v0, ref v1, ref v0, ref v2, ref color); } else { data.NDotL0 = nl1; data.NDotL1 = nl2; data.NDotL2 = nl0; data.NDotL3 = nl2; ProcessScanline(ref data, ref v1, ref v2, ref v0, ref v2, ref color); } } } }
void ProcessScanline(ref ScanlineData d, ref Vertex v0, ref Vertex v1, ref Vertex v2, ref Vertex v3, ref Color4 color) { var p0 = v0.Coordinates; var p1 = v1.Coordinates; var p2 = v2.Coordinates; var p3 = v3.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 = p0.Y != p1.Y ? (d.Y - p0.Y) / (p1.Y - p0.Y) : 1; var gradient2 = p2.Y != p3.Y ? (d.Y - p2.Y) / (p3.Y - p2.Y) : 1; var sx = (int)gradient1.Interpolate(p0.X, p1.X); var ex = (int)gradient2.Interpolate(p2.X, p3.X); // starting Z & ending Z var z1 = gradient1.Interpolate(p0.Z, p1.Z); var z2 = gradient2.Interpolate(p2.Z, p3.Z); var snl = gradient1.Interpolate(d.NDotL0, d.NDotL1); var enl = gradient2.Interpolate(d.NDotL2, d.NDotL3); Color c = new Color(); Color4 c4; Vector3 pt; // drawing a line from left (sx) to right (ex) for (var x = sx; x < ex; x++) { var gradient = (x - sx) / (float)(ex - sx); var z = gradient.Interpolate(z1, z2); var ndotl = gradient.Interpolate(snl, enl); Color4.Scale(ref color, ndotl, out c4); c.FromColor4(ref c4); pt.X = x; pt.Y = d.Y; pt.Z = z; DrawPoint(ref pt, ref c); } }