public abstract IEnumerable<FragmentQuad> Rasterize(InputAssemblerPrimitiveOutput primitive);
public override IEnumerable<FragmentQuad> Rasterize(InputAssemblerPrimitiveOutput primitive) { _primitive = primitive; _p0 = _primitive.Vertices[0].Position; _p1 = _primitive.Vertices[1].Position; _p2 = _primitive.Vertices[2].Position; // Precompute alpha, beta and gamma denominator values. These are the same for all fragments. _alphaDenominator = ComputeFunction(_p0.X, _p0.Y, ref _p1, ref _p2); _betaDenominator = ComputeFunction(_p1.X, _p1.Y, ref _p2, ref _p0); _gammaDenominator = ComputeFunction(_p2.X, _p2.Y, ref _p0, ref _p1); // TODO: Handle degenerate triangles. The following code might be right, but need to check. //const float degenerateThreshold = 0.001f; //if (_alphaDenominator < degenerateThreshold || _betaDenominator < degenerateThreshold || _gammaDenominator < degenerateThreshold) // yield break; // Compute screen-space triangle bounding box. var screenBounds = Box2D.CreateBoundingBox(ref _p0, ref _p1, ref _p2); // Clip triangle bounding box to screen bounds. screenBounds = ScreenBounds.IntersectWith(ref screenBounds); // Calculate start and end positions. Because of the need to calculate derivatives // in the pixel shader, we require that fragment quads always have even numbered // coordinates (both x and y) for the top-left fragment. var startX = RoundDownToEven(screenBounds.MinX); var startY = RoundDownToEven(screenBounds.MinY); // Scan pixels in target area, checking if they are inside the triangle. // If they are, calculate the coverage. var maxX = screenBounds.MaxX; var maxY = screenBounds.MaxY; for (int y = startY; y < maxY; y += 2) for (int x = startX; x < maxX; x += 2) { if (FragmentFilter != null) { if (!FragmentFilter(x, y) && !FragmentFilter(x + 1, y) && !FragmentFilter(x, y + 1) && !FragmentFilter(x + 1, y + 1)) continue; } // First check whether any fragments in this quad are covered. If not, we don't // need to do any (expensive) interpolation of attributes. var fragmentQuad = new FragmentQuad { Fragment0 = CreateFragment(x, y, FragmentQuadLocation.TopLeft, ref screenBounds), Fragment1 = CreateFragment(x + 1, y, FragmentQuadLocation.TopRight, ref screenBounds), Fragment2 = CreateFragment(x, y + 1, FragmentQuadLocation.BottomLeft, ref screenBounds), Fragment3 = CreateFragment(x + 1, y + 1, FragmentQuadLocation.BottomRight, ref screenBounds), }; if (RasterizerState.IsMultisampleEnabled) { // For multisampling, we test coverage and interpolate attributes in two separate steps. // Check all samples to determine whether they are inside the triangle. bool covered0 = CalculateSampleCoverage(ref fragmentQuad.Fragment0); bool covered1 = CalculateSampleCoverage(ref fragmentQuad.Fragment1); bool covered2 = CalculateSampleCoverage(ref fragmentQuad.Fragment2); bool covered3 = CalculateSampleCoverage(ref fragmentQuad.Fragment3); if (!covered0 && !covered1 && !covered2 && !covered3) continue; // Otherwise, we do have at least one fragment with covered samples, so continue // with interpolation. We need to interpolate values for all fragments in this quad, // even though they may not all be covered, because we need all four fragments in order // to calculate derivatives correctly. InterpolateFragmentData(ref fragmentQuad.Fragment0); InterpolateFragmentData(ref fragmentQuad.Fragment1); InterpolateFragmentData(ref fragmentQuad.Fragment2); InterpolateFragmentData(ref fragmentQuad.Fragment3); } else { BarycentricCoordinates fragment0Coordinates, fragment1Coordinates, fragment2Coordinates, fragment3Coordinates; // For non-multisampling, we can re-use the same calculations for coverage and interpolation. bool covered0 = CalculateCoverageAndInterpolateFragmentData(ref fragmentQuad.Fragment0, out fragment0Coordinates); bool covered1 = CalculateCoverageAndInterpolateFragmentData(ref fragmentQuad.Fragment1, out fragment1Coordinates); bool covered2 = CalculateCoverageAndInterpolateFragmentData(ref fragmentQuad.Fragment2, out fragment2Coordinates); bool covered3 = CalculateCoverageAndInterpolateFragmentData(ref fragmentQuad.Fragment3, out fragment3Coordinates); if (!covered0 && !covered1 && !covered2 && !covered3) continue; // Create pixel shader input. fragmentQuad.Fragment0.Data = CreatePixelShaderInput(ref fragment0Coordinates); fragmentQuad.Fragment1.Data = CreatePixelShaderInput(ref fragment1Coordinates); fragmentQuad.Fragment2.Data = CreatePixelShaderInput(ref fragment2Coordinates); fragmentQuad.Fragment3.Data = CreatePixelShaderInput(ref fragment3Coordinates); } yield return fragmentQuad; } }