/// <summary> /// Writes a pixel to the buffer according to depth-test for single pass rendering. /// </summary> /// <param name="x">x index</param> /// <param name="y">y index</param> /// <param name="z">Depth buffer value</param> /// <param name="premultColor">Premultiplied Frame buffer value</param> /// <param name="premultTolerance">Premultiplied Tolerance buffer value</param> public void SetPixel(int x, int y, float z, Color premultColor, Color premultTolerance) { if (x >= 0 && x < width && y >= 0 && y < height && z >= 0 && z <= 1.0f) { bool isZFightingPossible = IsPixelRendered(x, y); bool drawPixel = DepthTest(z, zBuffer[x, y]); if (drawPixel) { // Write to frame buffer according to alpha value of pixel frameBuffer[x, y] = ColorOperations.PreMultipliedAlphaBlend( premultColor, frameBuffer[x, y]); // We have a pre-multiplied tolerance value toleranceBuffer[x, y] = ColorOperations.PreMultipliedToleranceBlend( premultTolerance, toleranceBuffer[x, y], premultColor.A); } // See if we were within tolerance of another test result if (isZFightingPossible) { if (DepthTestWithinTolerance(z, zBuffer[x, y], RenderTolerance.ZBufferTolerance)) { // If so, ignore this pixel, since we can't be sure if it's right toleranceBuffer[x, y] = Color.FromArgb(0, 255, 255, 255); } } else if (z < RenderTolerance.NearPlaneTolerance || 1.0 - RenderTolerance.FarPlaneTolerance < z) { // If we're right on the near/far clipping plane, we can't be sure if this pixel is right. // Note that the tolerance at the near plane will be considerably more than that of the // far plane if we are using a perspective camera to view this scene. toleranceBuffer[x, y] = Color.FromArgb(0, 255, 255, 255); } if (drawPixel && writeToZBuffer) { zBuffer[x, y] = z; } } }
/// <summary> /// Blend the contents of the RenderBuffer with a background color /// </summary> public void AddBackground(Color opaqueBackgroundColor) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // We keep our colors and tolerances premultiplied. // Do the tolerance blend first so that we have the correct alpha value from the framebuffer toleranceBuffer[x, y] = ColorOperations.PreMultipliedToleranceBlend( toleranceBuffer[x, y], toleranceBufferClearValue, frameBuffer[x, y].A); frameBuffer[x, y] = ColorOperations.PreMultipliedAlphaBlend( frameBuffer[x, y], opaqueBackgroundColor); } } }
/// <summary> /// Lights this pixel using precomputed lighting information. /// </summary> /// <param name="v">Interpolated vertex for this pixel position.</param> protected override void ComputePixelProgram(Vertex v) { bool rendered = false; Color totalTexturingTolerance = emptyColor; for (int pass = 0; pass < textures.Length; pass++) { // A Filter can be null if the Material or the Brush are null. // For those cases, we skip the material entirely. if (textures[pass] == null) { continue; } TextureFilter currentTexture = textures[pass]; rendered = true; // We need extra information for trilinear if (currentTexture is TrilinearTextureFilter) { ((TrilinearTextureFilter)currentTexture).MipMapFactor = v.MipMapFactor; } // Textures are not stored in premultiplied color space. // This means that we have to wait until we find the lookup tolerance before we can premultiply // (otherwise Alpha will be way off) Color texel = currentTexture.FilteredTextureLookup(v.TextureCoordinates); Color texelTolerance = emptyColor; if (currentTexture.HasErrorEstimation) { texelTolerance = currentTexture.FilteredErrorLookup( v.UVToleranceMin, v.UVToleranceMax, texel); } // Now we can premultiply. Color premultTexel = ColorOperations.PreMultiplyColor(texel); Color premultTexelTolerance = ColorOperations.PreMultiplyTolerance(texelTolerance, texel.A); // Modulate precomputed lighting (which is also premultiplied) by the Brush value Color premultColor = ColorOperations.Modulate(v.PrecomputedLight[pass], premultTexel); Color premultTolerance = ColorOperations.Modulate(v.PrecomputedLight[pass], premultTexelTolerance); // PrecomputedLightTolerance is NOT premultipled yet (see ComputeVertexProgram above). // This is because we needed to know the final alpha value of lighting * texture. // Color premultLightTolerance = ColorOperations.PreMultiplyTolerance(v.PrecomputedLightTolerance[pass], premultColor.A); premultTolerance = ColorOperations.Add(premultTolerance, premultLightTolerance); // For additive materials, we need to force the alpha channel to zero. // See notes on premultiplied blending in ColorOperations.cs if (currentTexture.MaterialType != MaterialType.Diffuse) { premultColor.A = 0x00; // Nothing needs to be done to tolerance's alpha // because the framebuffer's alpha value will be used in the blend } // we need to blend // Write to frame buffer according to alpha value of pixel v.Color = ColorOperations.PreMultipliedAlphaBlend(premultColor, v.Color); // Accumulate tolerance for each material pass totalTexturingTolerance = ColorOperations.PreMultipliedToleranceBlend( premultTolerance, totalTexturingTolerance, premultColor.A); } // Only set a pixel if we actually rendered at least one material for it ... if (rendered) { // Add texturing tolerance to our existing lighting tolerance. v.ColorTolerance = ColorOperations.Add(v.ColorTolerance, totalTexturingTolerance); // Send the pixel to be rendered buffer.SetPixel( (int)Math.Floor(v.ProjectedPosition.X), (int)Math.Floor(v.ProjectedPosition.Y), (float)v.ProjectedPosition.Z, v.Color, v.ColorTolerance ); } }