Viewport2DVisual3D blurry text on WPF controls - wpf

I'm attempting to host a WPF form on a Viewport2DVisual3D surface. I've set up the camera so that the controls fit the width of the window. The default geometry maps the entire form onto a square face, so it is necessary to do some sort of transformation to get the surface to look like a regular 2d form and not appear stretched vertically. The form looks okay overall but the text doesn't scale well, it is blurry and blocky and looks bad in different ways from line to line. Here's what I've tried to set the aspect ratio:
A ScaleTransform3D
Setting the mesh Positions to the proper aspect ratio
Setting the TextureCoordinates to the proper aspect ratio
The first two get me the results that I want, except for the blocky/blurry text. My conclusion at this point is that the font rendering is occurring before the form image is projected onto the 3d surface and then scaling occurs, so it will look bad no matter what. Does anyone know a way to work around this or to set it up right from the beginning? I don't know much about 3d graphics, just enough basic math to get the camera angles right, etc.
Have tested on Win 7 and XP.
Some of the resources I've used:
http://www.codeproject.com/KB/WPF/ContentControl3D.aspx
http://pavanpodila.spaces.live.com/blog/cns!9C9E888164859398!151.entry
A few snippets of the code:
<Viewport2DVisual3D.Geometry>
<MeshGeometry3D x:Name="FrontFaceGeometry"
Positions="-1,1,0 -1,-1,0 1,-1,0 1,1,0"
TextureCoordinates="0,0 0,1 1,1 1,0"
TriangleIndices="0 1 2 0 2 3"/>
</Viewport2DVisual3D.Geometry>
...
<Grid Width="500" x:Name="FrontFaceGrid">
Then in the Window_Loaded routine, e.g.
var aRatio = FrontFaceGrid.ActualHeight / FrontFaceGrid.ActualWidth;
FrontFaceGeometry.Positions[0] = new System.Windows.Media.Media3D.Point3D(-1, aRatio, 0);
FrontFaceGeometry.Positions[1] = new System.Windows.Media.Media3D.Point3D(-1, -aRatio, 0);
FrontFaceGeometry.Positions[2] = new System.Windows.Media.Media3D.Point3D(1, -aRatio, 0);
FrontFaceGeometry.Positions[3] = new System.Windows.Media.Media3D.Point3D(1, aRatio, 0);

To avoid the blurred text and other visual distortions make the 3D XY aspect ratio equal to the 2D control aspect ratio. This is achieved by setting X and Y MeshGeometry3D.Positions. For example, a 2D control sized at 500x700 could be mapped to a rectangle 3D mesh without distortion by assigning positions
<Viewport2DVisual3D.Geometry>
<MeshGeometry3D x:Name="FrontFaceGeometry"
Positions="-2.5,3.5,0 -2.5,-3.5,0 2.5,-3.5,0 2.5,3.5,0"
TextureCoordinates="0,0 0,1 1,1 1,0"
TriangleIndices="0 1 2 0 2 3"/>
</Viewport2DVisual3D.Geometry>
The image of the 2D control displayed within the 3D environment is always "stretched" to the mesh's dimensions.

You will be rendering the WPF form onto a texture on the square and then displaying the square using the GPU's texture engine. Depending on the mode the texture engine is using this could cause blockiness or blurriness (since the texture engine will try to interpolate the texture by default).
Why do you want to render it using a 3D visual and not normally if it is intended to fill the screen?

Related

WPF 3D x-coordinate appears backwards

Using WPF 3D, I define my geometry as follows:
<MeshGeometry3D Positions="0 0 0 1 1 0 1 0 0" />
My camera is defined as
<PerspectiveCamera FarPlaneDistance="20"
LookDirection="0,0,1"
UpDirection="0,1,0"
NearPlaneDistance="0"
Position="0,0,-10"
FieldOfView="45" />
However, when I look at the resulting picture, I get this:
It would appear that the X co-ordinate is backwards, i.e., the X axis is facing left. Curiously, when I try to flip the sign, i.e., when I write
<MeshGeometry3D Positions="0 0 0 -1 1 0 -1 0 0" />
The image disappears entirely. What's going on here?
I'm going to answer your second question first...3d engines like WPF usually employ backface culling, where faces that point away from the camera aren't rendered in order to improve performance. You can fix this by either changing the order of the points in your MeshGeometry3D or assigning a BackMaterial to your GeometryModel3D (as opposed to the regular Material).
With respect to your first question, it's not the X that's flipped around, it's the Z. If you look at the WPF 3D Graphics Overview on the Microsoft site you'll see that Z is negative going into the screen. You've set your camera at [0,0,-10] and set the look direction to [0,0,1], so the camera is effectively "behind" the object and looking backwards. Change these values to [0,0,10] and [0,0,-1], and add a BackMaterial like I mentioned above, and all will be good in the world again.

WPF 3D transparent textures - clipping?

http://www.youtube.com/watch?v=gZNdfVwkttM - you can see all of the problem described on this video if you can't see pictures.
All of walls in all images below have a semitransparent PNG texture. Each square wall, floor and ceiling tile is a separate GeometryModel3D (I know that is no good for performance but...). The floor and the ceiling of the central cube have no any geometry and textures - so they have a color the same as Window.Background (black). But the effect considered appears in any way of transparency obtaining: texture for ImageBrush with transparency, Material.Color (for example DiffuseMAterial.Color) where Color has alpha channel, ImageBrush as material where ImageBrush has Opacity - all the way I have the same problem.
All of walls consists of two triangles. Where are no explicit normals , because I define triangle indices so normals culculated automatically by WPF.
http://imagepost.ru/images/i/ma/image00001.png
It also haven't any back material or extra triangles from the back side.
As you can see there is no problem if you look only from +Z to -Z (standing on the blue square and looking to the red square - that is the second picture).
But if you look backward (from red to blue - the first picture) there is no transparency!
Well, I desided to look from the yellow square (third picture).
And then I walked nearer - you can see what was happening (pictures from 4 to 6).
There are no geometry construction error or texture mapping error or lighting error! It is some kind of clipping, I guessed! In addition there are some interested pictures 7 and 8 to prove my guess.
The last picture shows the white background of the window that hosted Viewport3D (previous was black), and my guess about clipping confirmed - WPF just not painted this part of the scene and we can see the window background!
BUT! If this happens from various looks, why the look from +Z to -Z (second picture) seems well?!
You need to sort the triangles based on their distances from the viewpoint. Only then, wpf will be able to blend the transparent textures.
DirectX is able to blend triangles on top of each other but only when drawing them back to front
http://www.ericsink.com/wpf3d/2_Transparency.html

How can I create beveled corners on a border in WPF?

I'm trying to do simple drawing in a subclass of a decorator, similar to what they're doing here...
How can I draw a border with squared corners in wpf?
...except with a single-pixel border thickness instead of the two they're using there. However, no matter what I do, WPF decides it needs to do its 'smoothing' (e.g. instead of rendering a single-pixel line, it renders a two-pixel line with each 'half' about 50% of the opacity.) In other words, it's trying to anti-alias the drawing. I do not want anti-aliased drawing. I want to say if I draw a line from 0,0 to 10,0 that I get a single-pixel-wide line that's exactly 10 pixels long without smoothing.
Now I know WPF does that, but I thought that's specifically why they introduced SnapsToDevicePixels and UseLayoutRounding, both of which I've set to 'True' in the XAML. I'm also making sure that the numbers I'm using are actual integers and not fractional numbers, but still I'm not getting the nice, crisp, one-pixel-wide lines I'm hoping for.
Help!!!
Mark
Aaaaah.... got it! WPF considers a line from 0,0 to 10,0 to literally be on that logical line, not the row of pixels as it is in GDI. To better explain, think of the coordinates in WPF being representative of the lines drawn on a piece of graph paper whereas the pixels are the squares those lines make up (assuming 96 DPI that is. You'd need to adjust accordingly if they are different.)
So... to get the drawing to refer to the pixel locations, we need to shift the drawing from the lines themselves to be the center of the pixels (squares on graph paper) so we shift all drawing by 0.5, 0.5 (again, assuming a DPI of 96)
So if it is a 96 DPI setting, simply adding this in the OnRender method worked like a charm...
drawingContext.PushTransform(new TranslateTransform(.5, .5));
Hope this helps others!
M
Have a look at this article: Draw lines exactly on physical device pixels
UPD
Some valuable quotes from the link:
The reason why the lines appear blurry, is that our points are center
points of the lines not edges. With a pen width of 1 the edges are
drawn excactly between two pixels.
A first approach is to round each point to an integer value (snap to a
logical pixel) an give it an offset of half the pen width. This
ensures, that the edges of the line align with logical pixels.
Fortunately the developers of the milcore (MIL stands for media
integration layer, that's WPFs rendering engine) give us a way to
guide the rendering engine to align a logical coordinate excatly on a
physical device pixels. To achieve this, we need to create a
GuidelineSet
protected override void OnRender(DrawingContext drawingContext)
{
Pen pen = new Pen(Brushes.Black, 1);
Rect rect = new Rect(20,20, 50, 60);
double halfPenWidth = pen.Thickness / 2;
// Create a guidelines set
GuidelineSet guidelines = new GuidelineSet();
guidelines.GuidelinesX.Add(rect.Left + halfPenWidth);
guidelines.GuidelinesX.Add(rect.Right + halfPenWidth);
guidelines.GuidelinesY.Add(rect.Top + halfPenWidth);
guidelines.GuidelinesY.Add(rect.Bottom + halfPenWidth);
drawingContext.PushGuidelineSet(guidelines);
drawingContext.DrawRectangle(null, pen, rect);
drawingContext.Pop();
}

WPF: How do I create a background that repeats horizontally without scaling?

I would like to create a background for my window which is an image that I want repeated horizontally. So far I've tried with the ImageBrush, but this option repeats the image horizontally and vertically. Also, I don't want it to scale when user resize the window, as it makes the image look funny.
If what you want to do is tile an image horizontally as you would in CSS with the simple one liner "background-repeat: repeat-x" then after some (!) trial and error what you need in XAML is this:
<ImageBrush ImageSource="Images/my-background-image.png"
TileMode="FlipY"
Stretch="Uniform"
AlignmentY="Top"
Viewport="0,0,90,3000"
ViewportUnits="Absolute" />
Where the last 2 values on the Viewport attribute are the width of your image in pixels and then a very large number that is higher than your viewport height so that the image is not repeated in the Y direction within that height.

Get Point3D relative to Mesh in WPF

I have a pretty simple 3D scene that is roughly built up like this:
<Viewport3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup x:Name="Group1">
<GeometryModel3D x:Name="m1">...</GeometryModel3D>
</Model3DGroup>
<Model3DGroup x:Name="Group2">
<GeometryModel3D x:Name="m2">...</GeometryModel3D>
</Model3DGroup>
<Model3DGroup x:Name="Group3">
<GeometryModel3D x:Name="m3">...</GeometryModel3D>
</Model3DGroup>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
And basically each of the 3 GeometryModel3D's has its own Mesh, Transforms, etc...
When I do hit testing on the Viewport3D, all I ever get is the point in 3D space that I clicked when I hit something. What I really am after, is the point ON the mesh that I hit in 3D space (not a single Point on the mesh, but rather the Point3D).
The goal for me is to add a new Material to the clicked on Mesh at the exact point that the user clicks. And I cannot do that if I only have access to the Point3D relative to the Viewport3D (because of all the transforms on the meshes etc...)
An example might be that the m1 Geometry is a rectangle like shape, with some slight rotations and transformed up the Y axis so its at the top of the screen.
When I click on the VERY bottom of the mesh, I would like to get the X-Y (Z not needed here) point relative to the local coordinate space of that mesh...
I hope that all makes sense, and thanks for any help you can give
Mark
I think what you're describing is actually two things:
Ray projection: given a hit-point on the 2D representation (i.e., the screen), cast a Ray from that point into your scene. This is alternatively called "Unproject", since it kind of relies on inverting the original screen transformation matrix and choosing some minimum and maximum "depth" values.
Ray/Mesh hit intersection: given a Ray and a Mesh, find where they meet. A slightly generalized variant is Ray/Sphere intersection Ray-Sphere Intersection
Check out this guy's blog, he has all the info I think you'll need:
Daniel Lehenbauer's Blog

Resources