internal static TextureFilter[] CreateTextures( List <Material> materials, Point originalMinUV, Point originalMaxUV, Rect screenSpaceBounds) { TextureFilter[] textures = new TextureFilter[materials.Count]; int materialCount = 0; foreach (Material material in materials) { if (material is DiffuseMaterial) { textures[materialCount] = TextureGenerator.RenderBrushToTextureFilter( ((DiffuseMaterial)material).Brush, originalMinUV, originalMaxUV, screenSpaceBounds); if (textures[materialCount] != null) { textures[materialCount].MaterialType = MaterialType.Diffuse; textures[materialCount].MaterialColor = ((DiffuseMaterial)material).Color; textures[materialCount].AmbientColor = ((DiffuseMaterial)material).AmbientColor; } } else if (material is SpecularMaterial) { textures[materialCount] = TextureGenerator.RenderBrushToTextureFilter( ((SpecularMaterial)material).Brush, originalMinUV, originalMaxUV, screenSpaceBounds); if (textures[materialCount] != null) { textures[materialCount].MaterialType = MaterialType.Specular; textures[materialCount].SpecularPower = ((SpecularMaterial)material).SpecularPower; textures[materialCount].MaterialColor = ((SpecularMaterial)material).Color; } } else if (material is EmissiveMaterial) { textures[materialCount] = TextureGenerator.RenderBrushToTextureFilter( ((EmissiveMaterial)material).Brush, originalMinUV, originalMaxUV, screenSpaceBounds); if (textures[materialCount] != null) { textures[materialCount].MaterialType = MaterialType.Emissive; textures[materialCount].MaterialColor = ((EmissiveMaterial)material).Color; } } materialCount++; } return(textures); }
private void RenderGeometry(ProjectedGeometry pg, Matrix3D view, Material material, VisibleFaces faces) { if (material == null) { return; } Triangle[] triangles = null; // Choose list based on triangle winding/facing switch (faces) { case VisibleFaces.Front: triangles = pg.FrontFaceTriangles; break; case VisibleFaces.Back: triangles = pg.BackFaceTriangles; break; default: throw new NotSupportedException("Cannot render these type of faces: " + faces.ToString()); } // We should only look into materials if we have any geometry to render. // Doing otherwise will throw exceptions when trying to use screen-space // bounds or UV coordinates. if (triangles.Length > 0) { // Create a flat, ordered list of textures to apply to this model List <Material> materials = ExtractMaterials(material); // Create a list of textures from the materials TextureFilter[] textures = TextureFilter.CreateTextures( materials, pg.OriginalMinUV, pg.OriginalMaxUV, Rect.Intersect(bounds.RenderBounds, pg.ScreenSpaceBounds)); // Use a precomputed light shader Shader shader = null; if (interpolation == InterpolationMode.Phong) { shader = new PrecomputedPhongShader(triangles, buffer, lights, textures, view); } else { shader = new PrecomputedGouraudShader(triangles, buffer, lights, textures, view); } shader.Rasterize(bounds.RenderBounds); RenderSilhouetteTolerance(pg, buffer, faces); } }
/// <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 ); } }
private static TextureFilter RenderBrushToTextureFilter(TileBrush brush, Point uvMin, Point uvMax, Rect screenSpaceBounds) { if (brush.ViewportUnits == BrushMappingMode.Absolute) { // Convert to relative (including extra tiling caused by repeating UV's) Matrix scale = GetConversionToRelative(uvMin, uvMax); Point tl = brush.Viewport.TopLeft * scale; Point br = brush.Viewport.BottomRight * scale; brush.ViewportUnits = BrushMappingMode.RelativeToBoundingBox; brush.Viewport = new Rect(tl, br); } // double desiredWidth = 0; double desiredHeight = 0; if (brush is ImageBrush) { ImageBrush ib = (ImageBrush)brush; desiredWidth = ((BitmapSource)ib.ImageSource).PixelWidth; desiredHeight = ((BitmapSource)ib.ImageSource).PixelHeight; desiredWidth = desiredHeight = MathEx.Length(desiredWidth, desiredHeight); } else if (brush is DrawingBrush || brush is VisualBrush) { // We don't know how big to make it because WPF can scale the content to an arbitrary size // and still look good (unlike images and their finite amount of data provided). // // If we make it too large, we will have more detail than WPF and if we make it too small, // we won't have enough detail. Making the image the as big as the diagonal of the screen // space bounds Rect seems to work best. desiredWidth = desiredHeight = MathEx.Length(screenSpaceBounds.Width, screenSpaceBounds.Height); } double uvWidth = uvMax.X - uvMin.X; double uvHeight = uvMax.Y - uvMin.Y; switch (brush.Stretch) { case Stretch.None: // Overwrite the previous computation and set bounds based on UV span desiredWidth = uvWidth; desiredHeight = uvHeight; break; case Stretch.Uniform: case Stretch.UniformToFill: // Match the aspect ratio of the brush content (currently at 1:1) if (uvWidth > uvHeight) { desiredWidth *= uvWidth / uvHeight; } else { desiredHeight *= uvHeight / uvWidth; } break; case Stretch.Fill: // desiredWidth/Height are already correct break; } BitmapSource image = RenderBrushToImageData(brush, (int)desiredWidth, (int)desiredHeight); //// Avalon chooses different filtering based on brush type and tiling mode: //// ImageBrush -> Trilinear //// DrawingBrush -> Bilinear see Change 164703 by REDMOND\milesc on 2006/03/28 20:37:04 //// VisualBrush -> Bilinear see Change 164703 by REDMOND\milesc on 2006/03/28 20:37:04 TextureFilter filter = (brush is ImageBrush) ? new TrilinearTextureFilter(image) : new BilinearTextureFilter(image); // Perform a texture lookup error estimation filter.HasErrorEstimation = true; return(filter); }