简体   繁体   中英

How do you do a 3D transform (perspective) in C# or VB.Net?

What I am looking to do sounds really simple, but no where on the Internet so far have I found a way to do this in DotNet nor found a 3rd party component that does this either (without spending thousands on completely unnecessary features). Here goes:

I have a jpeg of a floor tile (actual photo) that I create a checkerboard pattern with. In dotnet, it is easy to rotate and stitch photos together and save the final image as a jpeg.

Next, I want to take that final picture and make it appear as if the "tiles" are laying on a floor for a generic "room scene". Basically adding a 3D perspective to make it appear as if it is actually in the room scene.

Heres a website that is doing something similar with carpeting, however I need to do this in a WinForms application: Flor Website

Basically, I need to create a 3D perspective of a jpeg, then save it as a new jpeg (then I can put an overlay of the generic room scene).

Anyone have any idea on where to get a 3rd party DotNet image processing module that can do this seemingly simple task?

It is not so simple because you need a 3D transformation, which is more complicated and computationally expensive than a simple 2D transformation such as rotation, scaling or shearing. For you to have an idea of the difference in the math, 2D transformations require 2 by 2 matrices, whereas a projection transformation (which is more complicated than other 3D transforms) requires a 4 by 4 matrix...

What you need is some 3D rendering engine in which you can draw polygons (in a perspective view) and them cover them with a texture (like a carpet). For .Net 2.0, I'd recommend using SlimDX which is a port of DirectX that would allow you to render polygons, but there is some learning curve. If you are using WPF (.Net 3.0 and up), there is a built in 3D canvas that allows you to draw textured polygons in perspective. That might be easier/better to learn than SlimDX for your purposes. I'm sure that there is a way to redirect the output of the 3D canvas towards a jpeg...

You might simplify the problem a lot if you don't require great performance and if you restrict the orientation of the texture (eg. always a horizontal floor or always a vertical wall). If so, you could probably render it yourself with a simple drawing loop in .Net 2.0.

If you just want a plain floor, your code would look like this. WARNING: Obtaining your desired results will take some significant time and refinement, specially if you don't know the math very well. But on the other hand, it is always fun to play with code of this type... (:

Find some sample images below.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace floorDrawer
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        ResizeRedraw = DoubleBuffered = true;

        Width = 800;
        Height = 600;

        Paint += new PaintEventHandler(Form1_Paint);

    }

    void Form1_Paint(object sender, PaintEventArgs e)
    {

        // a few parameters that control the projection transform
        // these are the parameters that you can modify to change 
        // the output

        double cz = 10; // distortion
        double m = 1000; // magnification, usually around 1000 (the pixel width of the monitor)

        double y0 = -100; // floor height

        string texturePath = @"c:\pj\Hydrangeas.jpg";//@"c:\pj\Chrysanthemum.jpg";


        // screen size
        int height = ClientSize.Height;
        int width = ClientSize.Width;

        // center of screen
        double cx = width / 2;
        double cy = height / 2;



        // render destination
        var dst = new Bitmap(width, height);

        // source texture

        var src = Bitmap.FromFile(texturePath) as Bitmap;

        // texture dimensions
        int tw = src.Width;
        int th = src.Height;

        for (int y = 0; y < height; y++)
            for (int x = 0; x < width; x++)
            {
                double v = m * y0 / (y - cy) - cz;
                double u = (x - cx) * (v + cz) / m;

                int uu = ((int)u % tw + tw) % tw;
                int vv = ((int)v % th + th) % th;
                // The following .SetPixel() and .GetPixel() are painfully slow
                // You can replace this whole loop with an equivalent implementation
                // using pointers inside unsafe{} code to make it much faster.
                // Note that by casting u and v into integers, we are performing
                // a nearest pixel interpolation...  It's sloppy but effective.

                dst.SetPixel(x, y, src.GetPixel(uu, vv));
            }

        // draw result on the form
        e.Graphics.DrawImage(dst, 0, 0);
    }

}
}

这是使用Windows 7示例图像之一的示例输出。

使用另一个Windows 7示例图片的另一个示例。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM