public void DrawTopTriangle(Vertex v1, Vertex v2, Vertex v3, Scene scene, VertexTriangle vt, VertexTriangle oriVt) { Vector4 p1 = v1.ScreenSpacePosition; Vector4 p2 = v2.ScreenSpacePosition; Vector4 p3 = v3.ScreenSpacePosition; Color4 color1 = v1.Color; Color4 color2 = v2.Color; Color4 color3 = v3.Color; float ax = p1.X; float bx = p2.X; float cx = p3.X; float ay = p1.Y; float by = p2.Y; float cy = p3.Y; float az = p1.Z; float bz = p2.Z; float cz = p3.Z; Vector4 normal1 = v1.nowNormal; Vector4 normal2 = v2.nowNormal; Vector4 normal3 = v3.nowNormal; oriVt.PreCalWeight(); if (bx == cx) { for (int y = (int)ay; y <= (int)cy; y++) { float y1 = (float)(y - ay) / (float)(cy - ay); float y2 = (float)(y - by) / (float)(cy - by); y1 = MathUtil.Clamp01(y1); y2 = MathUtil.Clamp01(y2); int x0 = (int)MathUtil.Interp(ax, cx, y1); int x1 = (int)bx; float z1 = MathUtil.Interp(az, cz, y1); float z2 = bz; Color4 c1 = new Color4(); Color4 c2 = new Color4(); if (scene.renderState == Scene.RenderState.GouraduShading) { c1 = MathUtil.ColorInterp(color1, color3, y1); c2 = MathUtil.ColorInterp(color2, color3, y2); } Vector4 n1 = new Vector4(0, 0, 0, 0); Vector4 n2 = new Vector4(0, 0, 0, 0); if (DirectionLight.IsEnable) { n1 = MathUtil.Vector4Interp(normal1, normal3, y1); n2 = MathUtil.Vector4Interp(normal2, normal3, y2); } for (int x = x0; x <= x1; x++) { float r3 = (float)(x - x0) / (float)(x1 - x0); r3 = MathUtil.Clamp01(r3); float z = MathUtil.Interp(z1, z2, r3); Vector4 pos = new Vector4(x, y, z, 0); switch (scene.renderState) { case Scene.RenderState.WireFrame: { } break; case Scene.RenderState.GouraduShading: { oriVt.CalWeight(pos); Color4 c3 = MathUtil.ColorInterp(c1, c2, r3); if (DirectionLight.IsEnable) { Vector4 n3 = MathUtil.Vector4Interp(n1, n2, r3); n3 = oriVt.GetNormal(); final = DirectionLight.GetFinalLightColor(n3, c3); } else { final = c3; } } break; case Scene.RenderState.TextureMapping: { oriVt.CalWeight(pos); Vector4 n3 = MathUtil.Vector4Interp(n1, n2, r3); Vector4 uv = oriVt.GetInterUV(); if (DirectionLight.IsEnable) { Color4 c = device.Tex2D(uv.X, uv.Y, scene.mesh.texture); n3 = oriVt.GetNormal(); final = DirectionLight.GetFinalLightColor(n3, c); } else { final = device.Tex2D(uv.X, uv.Y, scene.mesh.texture); } } break; } device.DrawPoint(pos, final); } } } else if (ax == cx) { for (int y = (int)ay; y <= (int)cy; y++) { float y1 = (float)(y - ay) / (float)(cy - ay); float y2 = (float)(y - by) / (float)(cy - by); y1 = MathUtil.Clamp01(y1); y2 = MathUtil.Clamp01(y2); int x0 = (int)ax; int x1 = (int)MathUtil.Interp(bx, cx, y2); float z1 = MathUtil.Interp(az, cz, y1); float z2 = MathUtil.Interp(bz, cz, y2); Color4 c1 = new Color4(); Color4 c2 = new Color4(); if (scene.renderState == Scene.RenderState.GouraduShading) { c1 = MathUtil.ColorInterp(color1, color3, y1); c2 = MathUtil.ColorInterp(color2, color3, y2); } Vector4 n1 = new Vector4(0, 0, 0, 0); Vector4 n2 = new Vector4(0, 0, 0, 0); if (DirectionLight.IsEnable) { n1 = MathUtil.Vector4Interp(normal1, normal3, y1); n2 = MathUtil.Vector4Interp(normal2, normal3, y2); } for (int x = x0; x < x1; x++) { float r3 = (float)(x - x0) / (float)(x1 - x0); r3 = MathUtil.Clamp01(r3); float z = MathUtil.Interp(z1, z2, r3); Vector4 pos = new Vector4(x, y, z, 0); switch (scene.renderState) { case Scene.RenderState.WireFrame: { } break; case Scene.RenderState.GouraduShading: { oriVt.CalWeight(pos); Color4 c3 = MathUtil.ColorInterp(c1, c2, r3); if (DirectionLight.IsEnable) { Vector4 n3 = MathUtil.Vector4Interp(n1, n2, r3); n3 = oriVt.GetNormal(); final = DirectionLight.GetFinalLightColor(n3, c3); } else { final = c3; } } break; case Scene.RenderState.TextureMapping: { oriVt.CalWeight(pos); Vector4 n3 = MathUtil.Vector4Interp(n1, n2, r3); Vector4 uv = oriVt.GetInterUV(); if (DirectionLight.IsEnable) { Color4 c = device.Tex2D(uv.X, uv.Y, scene.mesh.texture); n3 = oriVt.GetNormal(); final = DirectionLight.GetFinalLightColor(n3, c); } else { final = device.Tex2D(uv.X, uv.Y, scene.mesh.texture); } } break; } device.DrawPoint(pos, final); } } } else { for (int y = (int)ay; y <= cy; y++) { float y1 = (float)(y - ay) / (float)(cy - ay); y1 = MathUtil.Clamp01(y1); float y2 = (y - by) / (cy - by); y2 = MathUtil.Clamp01(y2); float x0 = (int)MathUtil.Interp(ax, cx, y1); float x1 = (int)MathUtil.Interp(bx, cx, y2); float z1 = MathUtil.Interp(az, cz, y1); float z2 = MathUtil.Interp(bz, cz, y2); Color4 c1 = new Color4(); Color4 c2 = new Color4(); if (scene.renderState == Scene.RenderState.GouraduShading) { c1 = MathUtil.ColorInterp(color1, color3, y1); c2 = MathUtil.ColorInterp(color2, color3, y2); } Vector4 n1 = new Vector4(0, 0, 0, 0); Vector4 n2 = new Vector4(0, 0, 0, 0); if (DirectionLight.IsEnable) { n1 = MathUtil.Vector4Interp(normal1, normal3, y1); n2 = MathUtil.Vector4Interp(normal2, normal3, y2); } for (float x = x0; x < x1; x++) { float r3 = (float)(x - x0) / (float)(x1 - x0); r3 = MathUtil.Clamp01(r3); float z = MathUtil.Interp(z1, z2, r3); //Color4 c3 = MathUtil.ColorInterp(c1, c2, r3); Vector4 pos = new Vector4(x, y, z, 0); switch (scene.renderState) { case Scene.RenderState.WireFrame: { } break; case Scene.RenderState.GouraduShading: { Color4 c3 = MathUtil.ColorInterp(c1, c2, r3); oriVt.CalWeight(pos); if (DirectionLight.IsEnable) { Vector4 n3 = MathUtil.Vector4Interp(n1, n2, r3); n3 = oriVt.GetNormal(); final = DirectionLight.GetFinalLightColor(n3, c3); } else { final = c3; } } break; case Scene.RenderState.TextureMapping: { oriVt.CalWeight(pos); Vector4 n3 = MathUtil.Vector4Interp(n1, n2, r3); Vector4 uv = oriVt.GetInterUV(); if (DirectionLight.IsEnable) { Color4 c = device.Tex2D(uv.X, uv.Y, scene.mesh.texture); n3 = oriVt.GetNormal(); final = DirectionLight.GetFinalLightColor(n3, c); } else { final = device.Tex2D(uv.X, uv.Y, scene.mesh.texture); } } break; } device.DrawPoint(pos, final); } } } }
public static void GouraudColor(Scene scene, Device device, int i, Edge a1, Edge a2, VertexTriangle orivt, VertexTriangle vt, bool isworld) { Color4 final = new Color4(); /* * 记过排序后的边传入连续两条边 相交的情况只有两种钝角三角形 和锐角三角形 * x 从小到大 * * /\ /\ * / \ / \ * / \/ \_________ * x1 x3 x4 * 1. x左右分开 * 2. x相同 以斜率排序 * */ /* 双线性插值算法 * * */ Vector4 screenA1V1 = a1.v1.ScreenSpacePosition; Vector4 screenA1V2 = a1.v2.ScreenSpacePosition; Vector4 screenA2V1 = a2.v1.ScreenSpacePosition; Vector4 screenA2V2 = a2.v2.ScreenSpacePosition; // 双线性插值系数 // 第一次插值算出交点的颜色系数 float r1 = ((float)i - screenA1V1.Y) / (float)(screenA1V2.Y - screenA1V1.Y); float r2 = ((float)i - screenA2V1.Y) / (float)(screenA2V2.Y - screenA2V1.Y); r1 = MathUtil.Clamp01(r1); r2 = MathUtil.Clamp01(r2); // 像素点深度插值 float z1 = MathUtil.Interp(screenA1V1.Z, screenA1V2.Z, r1); float z2 = MathUtil.Interp(screenA2V1.Z, screenA2V2.Z, r2); //float z3 = 0; float r3 = MathUtil.Clamp01(((float)x - a1.x) / (a2.x - a1.x)); //float r3 = (float)(x - Math.Floor(a1.x)) / (a2.x - a1.x); float z = MathUtil.Interp(z1, z2, r3); //float z = vt.GetInterValue(z1, z2, z3); float nDotL1 = 0, nDotL2 = 0; switch (scene.renderState) { case Scene.RenderState.WireFrame: { } break; case Scene.RenderState.GouraduShading: { } break; case Scene.RenderState.TextureMapping: { orivt.CalWeight(new Vector4(x, i, 0, 0)); Vector4 uv = orivt.GetInterUV(); if (isworld) { //final = device.Tex2D(uv.X, uv.Y, scene.worldMap.texture); } else { if (DirectionLight.IsEnable) { float nDotLA1V1 = DirectionLight.ComputeNDotL(a1.v1.nowPos, a1.v1.nowNormal); float nDotLA1V2 = DirectionLight.ComputeNDotL(a1.v2.nowPos, a1.v2.nowNormal); float nDotLA2V1 = DirectionLight.ComputeNDotL(a2.v1.nowPos, a2.v1.nowNormal); float nDotLA2V2 = DirectionLight.ComputeNDotL(a2.v2.nowPos, a2.v2.nowNormal); nDotL1 = MathUtil.Interp(nDotLA1V1, nDotLA1V2, r1); nDotL2 = MathUtil.Interp(nDotLA2V1, nDotLA2V2, r2); float nDotL = MathUtil.Interp(nDotL1, nDotL2, r3); //Console.WriteLine(nDotL1+" "+ nDotL2+" " + nDotL); final = device.Tex2D(uv.X, uv.Y, scene.mesh.texture); final = final * DirectionLight.GetFinalLightColor(nDotL); } else { final = device.Tex2D(uv.X, uv.Y, scene.mesh.texture); } } } break; } device.DrawPoint(new Vector4(x, i, z, 0), final); }
/* 边表法填充 * 优点 扫描效率高,结构清晰 * 缺点 光栅化时三角形斜边在水平情况下差值不均匀造成颜色区块 * vt是裁剪后的三角形, * orivt是原始三角形,目的是在纹理映射的时候计算权重 * */ public void ProcessScanLine(VertexTriangle vt, VertexTriangle oriVt, Scene scene, bool isworld) { int yMin = this.height; int yMax = 0; Vertex A = vt.Vertices[0]; Vertex B = vt.Vertices[1]; Vertex C = vt.Vertices[2]; /* 扫描线原理 * * 1. 求扫描线与多边形的交点 * 2. 对所求得的交点按 x 从小到大排序 * 3. 将交点两两配对,并填充每一区段 * */ // ET AEL 扫描算法见 72 页 4.2.2 /* 1. 找到三角形 y 的最大值和最小值 * 并将三角形的边以顶点坐标X的值从左往右(x 从小到大)填充 * 在边表 ET[] 中对应三角形的Ymin处 */ Vertex[] vertices = vt.Vertices; for (int i = 0; i < vertices.Length; i++) { for (int j = i + 1; j < vertices.Length; j++) { Vector4 screen1 = vertices[i].ScreenSpacePosition; Vector4 screen2 = vertices[j].ScreenSpacePosition; if ((int)screen1.Y != (int)screen2.Y) { if (screen1.Y > yMax) { yMax = (int)screen1.Y; } if (screen2.Y > yMax) { yMax = (int)screen2.Y; } if (screen1.Y < yMin) { yMin = (int)screen1.Y; } if (screen2.Y < yMin) { yMin = (int)screen2.Y; } if (yMax > this.height) { yMax = this.height; } if (yMin < 0) { yMin = 0; } int x1 = (int)screen1.X; int y1 = (int)screen1.Y; int x2 = (int)screen2.X; int y2 = (int)screen2.Y; int ymin = y1 > y2 ? y2 : y1; if (ymin < 0) { ymin = 0; } int ymax = y1 > y2 ? y1 : y2; if (ymax > this.height) { ymax = this.height; } // 保存的边的上端点x坐标 float x = y1 > y2 ? x2 : x1; //边的斜率的倒数 float dx = (float)(x1 - x2) * 1.0f / (float)(y1 - y2); Edge e = new Edge(); e.yMax = ymax; // 保存x的目的是为了插入时保证边的顺序是按照x从小到大插入 // 在AEL中表示当前扫描线与边的交点x坐标,初值(在ET中的值)为边的下端点的x坐标 e.x = x; // 斜率是保证在插入时,如果 x相同,以斜率大小比较 e.deltaX = dx; // 保存边的上端点 e.v1 = y1 > y2 ? vertices[j] : vertices[i]; // 保存边的下端点 e.v2 = y1 > y2 ? vertices[i] : vertices[j]; EdgeTable.InsertEdge(ref ET[ymin].nextEdge, e); } } } /* 2. 置空活动边表 AEL * AEL 存储的是 ET表在 扫描线 y = i 有交点的三角形的边 */ AEL = new Edge(); oriVt.PreCalWeight(); for (int i = yMin; i < yMax; i++) { // 将边表中在 y = i 的边 插入活动边表AEL中,并删除边表里的边 EdgeTable.SearchTable(i, ET, AEL); if (AEL.nextEdge == null) { continue; } // 获取连续的两条边 Edge a1 = (Edge)AEL.nextEdge.Clone(); Edge a2 = (Edge)AEL.nextEdge.nextEdge.Clone(); // 从ymin开始扫描填充 /* * 记过排序后的边传入连续两条边 相交的情况只有两种钝角三角形 和锐角三角形 * x 从小到大 * * /\ /\ * / \ / \ * / \/ \_________ * x1 x3 x4 * 1. x左右分开 * 2. x相同 以斜率排序 * */ /* 双线性插值算法 * 斜边一次差值找出交点 * 水平扫描差值找出当前点 */ Vector4 screenA1V1 = a1.v1.ScreenSpacePosition; Vector4 screenA1V2 = a1.v2.ScreenSpacePosition; Vector4 screenA2V1 = a2.v1.ScreenSpacePosition; Vector4 screenA2V2 = a2.v2.ScreenSpacePosition; // 第一次插值算出交点的颜色系数 float r1 = ((float)i - screenA1V1.Y) / (float)(screenA1V2.Y - screenA1V1.Y); float r2 = ((float)i - screenA2V1.Y) / (float)(screenA2V2.Y - screenA2V1.Y); r1 = MathUtil.Clamp01(r1); r2 = MathUtil.Clamp01(r2); // 像素点深度插值 float z1 = MathUtil.Interp(screenA1V1.Z, screenA1V2.Z, r1); float z2 = MathUtil.Interp(screenA2V1.Z, screenA2V2.Z, r2); float nDotLA1V1 = 0, nDotLA1V2 = 0, nDotLA2V1 = 0, nDotLA2V2 = 0, nDotL1 = 0, nDotL2 = 0; if (DirectionLight.IsEnable) { nDotLA1V1 = DirectionLight.ComputeNDotL(a1.v1.nowPos, a1.v1.nowNormal); nDotLA1V2 = DirectionLight.ComputeNDotL(a1.v2.nowPos, a1.v2.nowNormal); nDotLA2V1 = DirectionLight.ComputeNDotL(a2.v1.nowPos, a2.v1.nowNormal); nDotLA2V2 = DirectionLight.ComputeNDotL(a2.v2.nowPos, a2.v2.nowNormal); // 双线性插值系数 nDotL1 = MathUtil.Interp(nDotLA1V1, nDotLA1V2, r1); nDotL2 = MathUtil.Interp(nDotLA2V1, nDotLA2V2, r2); } // 横向填充 while (a1 != null && a2 != null) { for (int x = (int)AEL.nextEdge.x; x < (int)AEL.nextEdge.nextEdge.x; x++) { float r3 = MathUtil.Clamp01(((float)x - a1.x) / (a2.x - a1.x)); float z = MathUtil.Interp(z1, z2, r3); switch (scene.renderState) { case Scene.RenderState.WireFrame: { } break; case Scene.RenderState.GouraduShading: { //Padding.x = x; //Padding.GouraudColor(scene,device,x,i,z,vt,oriVt); } break; case Scene.RenderState.TextureMapping: { oriVt.CalWeight(new Vector4(x, i, 0, 0)); Vector4 uv = oriVt.GetInterUV(); if (isworld) { final = device.Tex2D(uv.X, uv.Y, scene.worldMap.texture); } else { if (DirectionLight.IsEnable) { float nDotL = MathUtil.Interp(nDotL1, nDotL2, r3); final = device.Tex2D(uv.X, uv.Y, scene.mesh.texture); final = final * DirectionLight.GetFinalLightColor(nDotL); } else { final = device.Tex2D(uv.X, uv.Y, scene.mesh.texture); } } } break; } device.DrawPoint(new Vector4(x, i, z, 0), final); } if (a2.nextEdge != null) { a1 = (Edge)a2.nextEdge.Clone(); a2 = (Edge)a1.nextEdge.Clone(); } else { break; } } // 删除y=yMax-1的边 , 边的特性是上开下闭, 所以 当扫描到y = yamx后,ymax以前的边和扫描线不会再有交点 // 所以要删除,避免盲目求交 // 同时算出 x 的下一个坐标 并保存在 AEL的第一个边中 p.nextEdge.x += p.nextEdge.deltaX; EdgeTable.DeleteEdge(AEL, i); } }