// Currently I have no better idea than to create an empty type, or you have to define all types yourself, somehow C# can not inferre the type here. /// <summary> /// Every parameter should be obvious (if you know OpenGL/DirectX a bit) /// except the varying parameter. This parameter is only here, so the C# /// can inferre the type of W automatically. If we omit this type here, /// you the specify all types for DrawTriangles by yourself. /// </summary> public void DrawTriangles <U, V, W>(VertexProgramDelegate <U, V, W> vertexProgram, FragmentProgramDelegate <U, W> fragmentProgram, U uniform, V[] vertices, W varying) where W : Varying { timerDrawTriangles.Start(); Vector4[] positions = new Vector4[3]; float[][] varyings = new float[3][]; int length = varying.Elements(); for (int i = 0; i < 3; i++) { varyings[i] = new float[length]; } for (int i = 0; i < vertices.Length; i += 3) { for (int j = 0; j < 3; j++) { positions[j] = vertexProgram(uniform, vertices[i + j], varying); positions[j].HomogenousDivide(); Viewport(ref positions[j]); varying.Store(varyings[j]); } // Sort the three vertices of a triangle along the y axis from top to down. Simplifies the method that does the real work. int i0 = 0; int i1 = 1; int i2 = 2; if (positions[i0].Y > positions[i1].Y) { Math.Swap(ref i0, ref i1); } if (positions[i1].Y > positions[i2].Y) { Math.Swap(ref i1, ref i2); } if (positions[i0].Y > positions[i1].Y) { Math.Swap(ref i0, ref i1); } RasterizeTriangle(positions[i0], positions[i1], positions[i2], varyings[i0], varyings[i1], varyings[i2], uniform, fragmentProgram, varying); } timerDrawTriangles.Stop(); }
private void RasterizeTriangle <U, W>(Vector4 p0, Vector4 p1, Vector4 p2, float[] v0, float[] v1, float[] v2, U uniforms, FragmentProgramDelegate <U, W> fragmentProgram, W varying) where W : Varying { // TODO no OpenGL fill convention int length = varying.Elements(); int width = this.colorBuffer.Width; int height = this.colorBuffer.Height; int x0 = (int)p0.X; int y0 = (int)p0.Y; float z0 = p0.Z; float w0 = p0.W; int x1 = (int)p1.X; int y1 = (int)p1.Y; float z1 = p1.Z; float w1 = p1.W; int x2 = (int)p2.X; int y2 = (int)p2.Y; float z2 = p2.Z; float w2 = p2.W; if (this.perspectiveCorrection) { for (int i = 0; i < length; i++) { v0[i] *= w0; v1[i] *= w1; v2[i] *= w2; } } float xslope0to1 = (float)(x0 - x1) / (float)(y0 - y1); float xslope0to2 = (float)(x0 - x2) / (float)(y0 - y2); float xslope1to2 = (float)(x1 - x2) / (float)(y1 - y2); // float xslope0to1 = (p0.X - p1.X) / (p0.Y - p1.Y); // float xslope0to2 = (p0.X - p2.X) / (p0.Y - p2.Y); // float xslope1to2 = (p1.X - p2.X) / (p1.Y - p2.Y); float zslope0to1 = (z0 - z1) / (y0 - y1); float zslope0to2 = (z0 - z2) / (y0 - y2); float zslope1to2 = (z1 - z2) / (y1 - y2); float wslope0to1 = (w0 - w1) / (y0 - y1); float wslope0to2 = (w0 - w2) / (y0 - y2); float wslope1to2 = (w1 - w2) / (y1 - y2); float[] vslope0to1 = new float[length]; float[] vslope0to2 = new float[length]; float[] vslope1to2 = new float[length]; for (int i = 0; i < length; i++) { vslope0to1[i] = (v0[i] - v1[i]) / (y0 - y1); vslope0to2[i] = (v0[i] - v2[i]) / (y0 - y2); vslope1to2[i] = (v1[i] - v2[i]) / (y1 - y2); } int x0to1; int x0to2; int x1to2; float z0to1; float z0to2; float z1to2; float w0to1; float w0to2; float w1to2; float[] v0to1 = new float[length]; float[] v0to2 = new float[length]; float[] v1to2 = new float[length]; int xmin; int xmax; float zmin; float zmax; float wmin; float wmax; float[] vmin; float[] vmax; float zslope; float wslope; float[] vslope = new float[length]; float z; float w; float[] v = new float[length]; // Theoretically more than one multiplication can be moved out of this loop. Practically it is slower. for (int y = y0; y < y1; y++) { x0to1 = (int)(x1 + xslope0to1 * (y - y1)); x0to2 = (int)(x2 + xslope0to2 * (y - y2)); z0to1 = z1 + zslope0to1 * (y - y1); z0to2 = z2 + zslope0to2 * (y - y2); w0to1 = w1 + wslope0to1 * (y - y1); w0to2 = w2 + wslope0to2 * (y - y2); for (int i = 0; i < length; i++) { v0to1[i] = v1[i] + vslope0to1[i] * (y - y1); v0to2[i] = v2[i] + vslope0to2[i] * (y - y2); } if (x0to1 > x0to2) { xmax = x0to1; xmin = x0to2; zmax = z0to1; zmin = z0to2; wmax = w0to1; wmin = w0to2; vmax = v0to1; vmin = v0to2; } else { xmax = x0to2; xmin = x0to1; zmax = z0to2; zmin = z0to1; wmax = w0to2; wmin = w0to1; vmax = v0to2; vmin = v0to1; } zslope = (zmax - zmin) / (xmax - xmin); wslope = (wmax - wmin) / (xmax - xmin); for (int i = 0; i < length; i++) { vslope[i] = (vmax[i] - vmin[i]) / (xmax - xmin); } for (int x = xmin; x < xmax; x++) { z = zmin + zslope * (x - xmin); w = wmin + wslope * (x - xmin); for (int i = 0; i < length; i++) { v[i] = vmin[i] + vslope[i] * (x - xmin); } w = 1.0f / w; if (this.perspectiveCorrection) { for (int i = 0; i < length; i++) { v[i] *= w; } } if ((x >= 0) && (y >= 0) && (x < width) && (y < height) && (z >= 0.0f) && (z <= 1.0f)) { if (z < depthBuffer.GetPixel(x, y).X) { varying.Load(v); colorBuffer.SetPixel(x, y, fragmentProgram(uniforms, varying)); depthBuffer.SetPixel(x, y, new Vector1(z)); } } } } for (int y = y1; y < y2; y++) { x1to2 = (int)(x2 + xslope1to2 * (y - y2)); x0to2 = (int)(x2 + xslope0to2 * (y - y2)); z1to2 = z2 + zslope1to2 * (y - y2); z0to2 = z2 + zslope0to2 * (y - y2); w1to2 = w2 + wslope1to2 * (y - y2); w0to2 = w2 + wslope0to2 * (y - y2); for (int i = 0; i < length; i++) { v1to2[i] = v2[i] + vslope1to2[i] * (y - y2); v0to2[i] = v2[i] + vslope0to2[i] * (y - y2); } if (x1to2 > x0to2) { xmax = x1to2; xmin = x0to2; zmax = z1to2; zmin = z0to2; wmax = w1to2; wmin = w0to2; vmax = v1to2; vmin = v0to2; } else { xmax = x0to2; xmin = x1to2; zmax = z0to2; zmin = z1to2; wmax = w0to2; wmin = w1to2; vmax = v0to2; vmin = v1to2; } zslope = (zmax - zmin) / (xmax - xmin); wslope = (wmax - wmin) / (xmax - xmin); for (int i = 0; i < length; i++) { vslope[i] = (vmax[i] - vmin[i]) / (xmax - xmin); } for (int x = xmin; x < xmax; x++) { z = zmin + zslope * (x - xmin); w = wmin + wslope * (x - xmin); for (int i = 0; i < length; i++) { v[i] = vmin[i] + vslope[i] * (x - xmin); } w = 1.0f / w; if (this.perspectiveCorrection) { for (int i = 0; i < length; i++) { v[i] *= w; } } if ((x >= 0) && (y >= 0) && (x < width) && (y < height) && (z >= 0.0f) && (z <= 1.0f)) { if (z < depthBuffer.GetPixel(x, y).X) { varying.Load(v); colorBuffer.SetPixel(x, y, fragmentProgram(uniforms, varying)); depthBuffer.SetPixel(x, y, new Vector1(z)); } } } } }