protected void DrawScreenSpaceTriangleInterpolated(PreparedTriangle triangle) { WriteableBitmap rt = _renderWindow.Framebuffer; if (triangle.GetScreenSpaceDirection() != VisibleTriangleDirection) { return; } var triangleArea = CalculateTriangleArea( triangle.Vertices[0].X, triangle.Vertices[0].Y, triangle.Vertices[1].X, triangle.Vertices[1].Y, triangle.Vertices[2].X, triangle.Vertices[2].Y); var vertexX = new double[] { triangle.Vertices[0].X, triangle.Vertices[1].X, triangle.Vertices[2].X }; var vertexY = new double[] { triangle.Vertices[0].Y, triangle.Vertices[1].Y, triangle.Vertices[2].Y }; var scanEdges = new List <ScanEdge>(); for (int current = 0; current < 3; ++current) { int next = (current + 1) % 3; double higherx, highery; double lowerx, lowery; if (vertexY[current] < vertexY[next]) { higherx = vertexX[current]; highery = vertexY[current]; lowerx = vertexX[next]; lowery = vertexY[next]; } else { higherx = vertexX[next]; highery = vertexY[next]; lowerx = vertexX[current]; lowery = vertexY[current]; } int minY = (int)highery; int maxY = (int)lowery; //int startX = (int)higherx; //int endX = (int)lowerx; if (minY == maxY) { continue; } var scanEdge = new ScanEdge(); scanEdge.MinimalY = (int)Math.Ceiling(highery); scanEdge.MaximalY = (int)Math.Ceiling(lowery); scanEdge.X = higherx; scanEdge.XSlope = (lowerx - higherx) / ((double)(lowery - highery)); scanEdges.Add(scanEdge); } if (scanEdges.Count == 0) { return; } scanEdges = scanEdges.OrderBy(t => t.MinimalY).ToList(); int scanEdgesActivated = 0; var startY = Math.Max(scanEdges.Min(t => t.MinimalY), 0); var endY = Math.Min(scanEdges.Max(t => t.MaximalY), rt.PixelHeight - 1); var activeEdges = new List <ScanEdge>(); for (var y = startY; y <= endY; ++y) { activeEdges = activeEdges.Where(t => y < t.MaximalY).ToList(); while (scanEdgesActivated < scanEdges.Count) { if (y < scanEdges[scanEdgesActivated].MinimalY) { break; } activeEdges.Add(scanEdges[scanEdgesActivated]); ++scanEdgesActivated; } activeEdges = activeEdges.OrderBy(t => t.X).ToList(); for (int i = 0; i < activeEdges.Count - 1; i += 2) { int startX = (int)Math.Ceiling(Math.Max(activeEdges[i].X, 0)); int endX = (int)Math.Floor(Math.Min(activeEdges[i + 1].X, rt.PixelWidth - 1)); for (int x = startX; x <= endX; ++x) { var aArea = CalculateTriangleArea( x, y, triangle.Vertices[1].X, triangle.Vertices[1].Y, triangle.Vertices[2].X, triangle.Vertices[2].Y); var bArea = CalculateTriangleArea( triangle.Vertices[0].X, triangle.Vertices[0].Y, x, y, triangle.Vertices[2].X, triangle.Vertices[2].Y); var cArea = CalculateTriangleArea( triangle.Vertices[0].X, triangle.Vertices[0].Y, triangle.Vertices[1].X, triangle.Vertices[1].Y, x, y); var fa = Math.Min(aArea / triangleArea, 1.0); var fb = Math.Min(bArea / triangleArea, 1.0); var fc = Math.Min(cArea / triangleArea, 1.0); if (fa + fb + fc > 1.0) { int lowestAreaIndex = 0; double lowestArea = fa; if (lowestArea > fb) { lowestAreaIndex = 1; lowestArea = fb; } if (lowestArea > fc) { lowestAreaIndex = 2; lowestArea = fc; } switch (lowestAreaIndex) { case 0: fa = 1.0 - fb - fc; break; case 1: fb = 1.0 - fa - fc; break; case 2: fc = 1.0 - fa - fb; break; } } var z = fa * triangle.Vertices[0].Z + fb * triangle.Vertices[1].Z + fc * triangle.Vertices[2].Z; if (z <= 1.0) { continue; } if (_zBuffer[x, y] > z) { continue; } _zBuffer[x, y] = z; // Pixel Shader Color outputColor = InterpolateColor( fa, triangle.Vertices[0].VertexColor, fb, triangle.Vertices[1].VertexColor, fc, triangle.Vertices[2].VertexColor); if (TexturingEnabled && Material.DiffuseTexture != null) { var u = fa * triangle.Vertices[0].U + fb * triangle.Vertices[1].U + fc * triangle.Vertices[2].U; var v = fa * triangle.Vertices[0].V + fb * triangle.Vertices[1].V + fc * triangle.Vertices[2].V; u = Math.Min(Math.Max(u, 0.0), 1.0); v = Math.Min(Math.Max(v, 0.0), 1.0); Color texColor = Material.DiffuseTexture.GetPixel((int)(u * Material.DiffuseTexture.PixelWidth), (int)(v * Material.DiffuseTexture.PixelHeight)); outputColor = MultiplyColors(outputColor, texColor); } // End of pixel shader rt.SetPixel(x, y, outputColor); } } activeEdges.ForEach(t => t.X += t.XSlope); } }
public void RenderObjMesh(ObjData meshToRender, Matrix <double> transformation, Matrix <double> normalTransformation) { if (Material == null) { Material = new Material(); } if (TexturingEnabled) { if (Material.DiffuseTexture != null) { Material.DiffuseTexture.Lock(); } } var hw = _renderWindow.Framebuffer.PixelWidth * 0.5; var hh = _renderWindow.Framebuffer.PixelHeight * 0.5; foreach (var triangle in meshToRender.Triangles) { var tri = new PreparedTriangle(); for (int i = 0; i < 3; ++i) { Vector <double> modelSpacePosition = meshToRender.Vertices[triangle.Vertices[i]]; Vector <double> modelSpaceNormal = meshToRender.Normals[triangle.Normals[i]]; Vector <double> screenSpacePosition = (transformation * modelSpacePosition.ExtendVector()) .ToCartesian() .Add(VectorHelpers.Create(1.0, 1.0, 0.0)) .PointwiseMultiply(VectorHelpers.Create(hw, hh, 1.0)); Vector <double> worldSpacePosition = (normalTransformation * modelSpacePosition.ExtendVector()) .ToCartesian(); Vector <double> worldSpaceNormal = (normalTransformation * modelSpaceNormal.ExtendVector(0.0)) .DiscardLastCoordinate().Normalize(2); // Vertex shader tri.Vertices[i].SetCoordinates(screenSpacePosition); tri.Vertices[i].SetTextureCoordinates(meshToRender.TexCoords[triangle.TexCoords[i]]); var vR = 0; var vG = 0; var vB = 0; if (AmbientLightingEnabled) { vR += Material.AmbientColor.R; vG += Material.AmbientColor.G; vB += Material.AmbientColor.B; } if (DiffuseLightingEnabled || SpecularLightingEnabled) { foreach (var light in Lights) { var lightDirection = (light.Position - worldSpacePosition).Normalize(2); if (DiffuseLightingEnabled) { var diffuseLightFactor = Math.Max(lightDirection.DotProduct(worldSpaceNormal), 0); var fullSaturationDiffuseColor = MultiplyColors(light.Color, Material.DiffuseColor); vR += (byte)(fullSaturationDiffuseColor.R * diffuseLightFactor); vG += (byte)(fullSaturationDiffuseColor.G * diffuseLightFactor); vB += (byte)(fullSaturationDiffuseColor.B * diffuseLightFactor); } if (SpecularLightingEnabled) { var eyeDirection = (WorldPositionEye - worldSpacePosition).Normalize(2); var reflected = (2 * worldSpaceNormal.DotProduct(lightDirection) * worldSpaceNormal - lightDirection).Normalize(2); var specularLightFactor = Math.Pow(Math.Max(eyeDirection.DotProduct(reflected), 0), Material.ShineFactor); var fullSaturationSpecularColor = MultiplyColors(light.Color, Material.SpecularColor); vR += (byte)(fullSaturationSpecularColor.R * specularLightFactor); vG += (byte)(fullSaturationSpecularColor.G * specularLightFactor); vB += (byte)(fullSaturationSpecularColor.B * specularLightFactor); } } } tri.Vertices[i].VertexColor = Color.FromRgb( (byte)Math.Min(vR, 255), (byte)Math.Min(vG, 255), (byte)Math.Min(vB, 255)); // End of vertex shader } DrawScreenSpaceTriangleInterpolated(tri); } if (TexturingEnabled && Material.DiffuseTexture != null) { Material.DiffuseTexture.Unlock(); } }