//draw scanline of a triangle. p1 and p2 is left line, p3 and p4 is right. y is the vertical slice of it. public void drawScanLine(ScanLineData data, Vertex v1, Vertex v2, Vertex v3, Vertex v4, Color c) { Vector3 p1 = v1.Coordinates; Vector3 p2 = v2.Coordinates; Vector3 p3 = v3.Coordinates; Vector3 p4 = v4.Coordinates; var gradient1 = p1.Y != p2.Y ? (data.currentY - p1.Y) / (p2.Y - p1.Y) : 1; var gradient2 = p3.Y != p4.Y ? (data.currentY - p3.Y) / (p4.Y - p3.Y) : 1; int sx = (int)interpolate(p1.X, p2.X, gradient1); int ex = (int)interpolate(p3.X, p4.X, gradient2); double sz = interpolate(p1.Z, p2.Z, gradient1); double ez = interpolate(p3.Z, p4.Z, gradient2); for (var x = sx; x < ex; x++) { double zg = (x - sx) / (double)(ex - sx); double z = interpolate(sz, ez, zg); Color shadedColor = Color.FromArgb(c.A, (int)(c.R * data.ndotl1), (int)(c.G * data.ndotl1), (int)(c.B * data.ndotl1)); drawPoint(x, data.currentY, z, c); } }
/// <summary> /// ProcessScanLine draws a line between 2 points from left to right /// papb -> pcpd /// pa, pb, pc, pd must be sorted before /// </summary> /// <param name="data"></param> /// <param name="va"></param> /// <param name="vb"></param> /// <param name="vc"></param> /// <param name="vd"></param> /// <param name="color"></param> public void ProcessScanLine(ScanLineData data, Vertex va, Vertex vb, Vertex vc, Vertex vd, Color color, Texture texture) { Vector3 pa = va.Coordinates; Vector3 pb = vb.Coordinates; Vector3 pc = vc.Coordinates; Vector3 pd = vd.Coordinates; 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; int sx = (int)Interpolate(pa.X, pb.X, gradient1); int ex = (int)Interpolate(pc.X, pd.X, gradient2); float z1 = Interpolate(pa.Z, pb.Z, gradient1); float z2 = Interpolate(pc.Z, pd.Z, gradient2); var snl = Interpolate(data.ndotla, data.ndotlb, gradient1); var enl = Interpolate(data.ndotlc, data.ndotld, gradient2); // Interpolate 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); for (var x = sx; x < ex; x++) { var gradient = (x - sx) / (float)(ex - sx); // Interpolationg Z, normal and texture coordinates on X var z = Interpolate(z1, z2, gradient); var ndotl = Interpolate(snl, enl, gradient); var u = Interpolate(su, eu, gradient); var v = Interpolate(sv, ev, gradient); Color textureColor; if (texture != null) { textureColor = texture.Map(u, v); } else { textureColor = Color.FromArgb(255, 255, 255, 255); } var r = textureColor.R * ndotl * (color.R / 255); var g = textureColor.G * ndotl * (color.G / 255); var b = textureColor.B * ndotl * (color.B / 255); var a = textureColor.A * ndotl * (color.A / 255); Color newColor; newColor = Color.FromArgb((int)a, (int)r, (int)g, (int)b); DrawPointVector3(new Vector3(x, data.currentY, z), newColor); } }
/// <summary> /// drawing line between 2 points from left to right /// papb -> pcpd /// pa, vb, vc, vd must then be sorted before /// </summary> void ProcessScanLine(ScanLineData data, Vertex va, Vertex vb, Vertex vc, Vertex vd, Color 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 == vb.Y or vc.Y == vd.Y, gradient is forced to 1 var gradient1 = System.Math.Abs(pa.Y - pb.Y) > float.MinValue ? (data.CurrentY - pa.Y) / (pb.Y - pa.Y) : 1; var gradient2 = System.Math.Abs(pc.Y - pd.Y) > float.MinValue ? (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); 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); var gradient = (x - sx) / (float)(ex - sx); var z = Interpolate(z1, z2, gradient); var ndotl = Interpolate(snl, enl, gradient); var u = Interpolate(su, eu, gradient); var v = Interpolate(sv, ev, gradient); var textureColor = texture?.Map(u, v) ?? Colors.Magenta; // 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), textureColor); } }
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; 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); var z1 = Interpolate(pa.Z, pb.Z, gradient1); var z2 = Interpolate(pc.Z, pd.Z, gradient2); var snl = Interpolate(data.ndotla, data.ndotlb, gradient1); var enl = Interpolate(data.ndotlc, data.ndotld, gradient2); 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); for (var x = sx; x < ex; x++) { var gradient = (x - sx) / (float)(ex - sx); var z = Interpolate(z1, z2, gradient); var ndotl = Interpolate(snl, enl, gradient); var u = Interpolate(su, eu, gradient); var v = Interpolate(sv, ev, gradient); var textureColor = texture?.Map(u, v) ?? new Color4(1, 1, 1, 1); DrawPoint(new Vector3(x, data.currentY, z), color * ndotl * textureColor); } }
public void DrawTriangle(Vertex v1, Vertex v2, Vertex v3, 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 (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; } Vector3 p1 = v1.Coordinates; Vector3 p2 = v2.Coordinates; Vector3 p3 = v3.Coordinates; // Light position Vector3 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 float nl1 = ComputeNDotL(v1.WorldCoordinates, v1.Normal, lightPos); float nl2 = ComputeNDotL(v2.WorldCoordinates, v2.Normal, lightPos); float nl3 = ComputeNDotL(v3.WorldCoordinates, v3.Normal, lightPos); var data = new ScanLineData { }; // computing lines' directions float dP1P2, dP1P3; // http://en.wikipedia.org/wiki/Slope // Computing 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; if (dP1P2 > dP1P3) { 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); } else { data.ndotla = nl1; data.ndotlb = nl3; data.ndotlc = nl2; data.ndotld = nl3; ProcessScanLine(data, v1, v3, v2, v3, color); } } } else { 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); } else { data.ndotla = nl2; data.ndotlb = nl3; data.ndotlc = nl1; data.ndotld = nl3; ProcessScanLine(data, v2, v3, v1, v3, color); } } } }
// drawing line between 2 points from left to right // papb -> pcpd // pa, pb, pc, pd must then be sorted before void ProcessScanLine(ScanLineData data, Vertex va, Vertex vb, Vertex vc, Vertex vd, Color4 color) { Vector3 pa = va.Coordinates; Vector3 pb = vb.Coordinates; Vector3 pc = vc.Coordinates; Vector3 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; int sx = (int)Interpolate(pa.X, pb.X, gradient1); int ex = (int)Interpolate(pc.X, pd.X, gradient2); // starting Z & ending Z float z1 = Interpolate(pa.Z, pb.Z, gradient1); float z2 = Interpolate(pc.Z, pd.Z, gradient2); var snl = Interpolate(data.ndotla, data.ndotlb, gradient1); var enl = Interpolate(data.ndotlc, data.ndotld, gradient2); // drawing a line from left (sx) to right (ex) for (var x = sx; x < ex; x++) { float gradient = (x - sx) / (float)(ex - sx); 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); } }
public void DrawTriangle(Vertex v1, Vertex v2, Vertex v3, Color4 color, Texture texture) { 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; var lightPos = new Vector3(0, 10, 10); 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(); float dP1P2, dP1P3; 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; } if (dP1P2 > dP1P3) { 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; data.ua = v1.TextureCoordinates.X; data.ub = v3.TextureCoordinates.X; data.uc = v1.TextureCoordinates.X; data.ud = v2.TextureCoordinates.X; data.va = v1.TextureCoordinates.Y; data.vb = v3.TextureCoordinates.Y; data.vc = v1.TextureCoordinates.Y; data.vd = v2.TextureCoordinates.Y; ProcessScanLine(data, v1, v3, v1, v2, color, texture); } else { data.ndotla = nl1; data.ndotlb = nl3; data.ndotlc = nl2; data.ndotld = nl3; data.ua = v1.TextureCoordinates.X; data.ub = v3.TextureCoordinates.X; data.uc = v2.TextureCoordinates.X; data.ud = v3.TextureCoordinates.X; data.va = v1.TextureCoordinates.Y; data.vb = v3.TextureCoordinates.Y; data.vc = v2.TextureCoordinates.Y; data.vd = v3.TextureCoordinates.Y; ProcessScanLine(data, v1, v3, v2, v3, color, texture); } } } else { 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; data.ua = v1.TextureCoordinates.X; data.ub = v2.TextureCoordinates.X; data.uc = v1.TextureCoordinates.X; data.ud = v3.TextureCoordinates.X; data.va = v1.TextureCoordinates.Y; data.vb = v2.TextureCoordinates.Y; data.vc = v1.TextureCoordinates.Y; data.vd = v3.TextureCoordinates.Y; ProcessScanLine(data, v1, v2, v1, v3, color, texture); } else { data.ndotla = nl2; data.ndotlb = nl3; data.ndotlc = nl1; data.ndotld = nl3; data.ua = v2.TextureCoordinates.X; data.ub = v3.TextureCoordinates.X; data.uc = v1.TextureCoordinates.X; data.ud = v3.TextureCoordinates.X; data.va = v2.TextureCoordinates.Y; data.vb = v3.TextureCoordinates.Y; data.vc = v1.TextureCoordinates.Y; data.vd = v3.TextureCoordinates.Y; ProcessScanLine(data, v2, v3, v1, v3, color, texture); } } } }
void DrawTriangle(Vertex v1, Vertex v2, Vertex v3, Color 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, 5000); // 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(); // computing lines' directions float dP1P2, dP1P3; // http://en.wikipedia.org/wiki/Slope // Computing 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++) { data.CurrentY = y; if (y < p2.Y) { data.Ndotla = nl1; data.Ndotlb = nl3; data.Ndotlc = nl1; data.Ndotld = nl2; data.Ua = v1.TexCoord.X; data.Ub = v3.TexCoord.X; data.Uc = v1.TexCoord.X; data.Ud = v2.TexCoord.X; data.Va = v1.TexCoord.Y; data.Vb = v3.TexCoord.Y; data.Vc = v1.TexCoord.Y; data.Vd = v2.TexCoord.Y; ProcessScanLine(data, v1, v3, v1, v2, color, texture); } else { data.Ndotla = nl1; data.Ndotlb = nl3; data.Ndotlc = nl2; data.Ndotld = nl3; data.Ua = v1.TexCoord.X; data.Ub = v3.TexCoord.X; data.Uc = v2.TexCoord.X; data.Ud = v3.TexCoord.X; data.Va = v1.TexCoord.Y; data.Vb = v3.TexCoord.Y; data.Vc = v2.TexCoord.Y; data.Vd = v3.TexCoord.Y; 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++) { data.CurrentY = y; if (y < p2.Y) { data.Ndotla = nl1; data.Ndotlb = nl2; data.Ndotlc = nl1; data.Ndotld = nl3; data.Ua = v1.TexCoord.X; data.Ub = v2.TexCoord.X; data.Uc = v1.TexCoord.X; data.Ud = v3.TexCoord.X; data.Va = v1.TexCoord.Y; data.Vb = v2.TexCoord.Y; data.Vc = v1.TexCoord.Y; data.Vd = v3.TexCoord.Y; ProcessScanLine(data, v1, v2, v1, v3, color, texture); } else { data.Ndotla = nl2; data.Ndotlb = nl3; data.Ndotlc = nl1; data.Ndotld = nl3; data.Ua = v2.TexCoord.X; data.Ub = v3.TexCoord.X; data.Uc = v1.TexCoord.X; data.Ud = v3.TexCoord.X; data.Va = v2.TexCoord.Y; data.Vb = v3.TexCoord.Y; data.Vc = v1.TexCoord.Y; data.Vd = v3.TexCoord.Y; ProcessScanLine(data, v2, v3, v1, v3, color, texture); } } } }
public void drawTri(Vertex v1, Vertex v2, Vertex v3, Color c) { Vector3 p1 = v1.Coordinates; Vector3 p2 = v2.Coordinates; Vector3 p3 = v3.Coordinates; //sort triangles by y if (p1.Y > p2.Y) { var temp = p2; p2 = p1; p1 = temp; } if (p2.Y > p3.Y) { var temp = p2; p2 = p3; p3 = temp; } if (p1.Y > p2.Y) { var temp = p2; p2 = p1; p1 = temp; } v1.Coordinates = p1; v2.Coordinates = p2; v3.Coordinates = p3; //face normal Vector3 faceNormal = (v1.Normal + v2.Normal + v3.Normal) / 3; Vector3 faceCenter = (v1.WorldCoordinates + v2.WorldCoordinates + v3.WorldCoordinates) / 3; Vector3 LightCoords = new Vector3(0, 10, 10); double ndotl = ComputeNDotL(faceCenter, faceNormal, LightCoords); var data = new ScanLineData { ndotl1 = ndotl }; //inverse slopes double dP1P2, dP1P3; 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; } if (dP1P2 > dP1P3) { for (var y = (int)p1.Y; y <= (int)p3.Y; y++) { data.currentY = y; if (y < p2.Y) { drawScanLine(data, v1, v3, v1, v2, c); } else { drawScanLine(data, v1, v3, v2, v3, c); } } } else { for (var y = (int)p1.Y; y <= (int)p3.Y; y++) { data.currentY = y; if (y < p2.Y) { drawScanLine(data, v1, v2, v1, v3, c); } else { drawScanLine(data, v2, v3, v1, v3, c); } } } }