你如何在C#或VB.Net中进行3D变换(透视)?

时间:2011-09-07 17:04:40

标签: c# .net vb.net 3d perspective

我想要做的事情听起来很简单,但到目前为止我没有在互联网上找到一种在DotNet中做到这一点的方法,也没有找到第三方组件来做到这一点(没有花费数千个完全不必要的功能) 。 这是:

我有一个地砖(实际照片)的jpeg,我用它创建了一个棋盘图案。 在dotnet中,可以轻松旋转和拼接照片,并将最终图像保存为jpeg。

接下来,我想拍摄最后一张照片,让它看起来好像“瓷砖”正在铺设在一个普通的“房间场景”的地板上。基本上添加一个3D透视图,使其看起来好像它实际上是在房间场景中。

这是一个与地毯类似的网站,但我需要在WinForms应用程序中执行此操作: Flor Website

基本上,我需要创建一个jpeg的3D透视图,然后将其保存为新的jpeg(然后我可以放置一般房间场景的叠加层)。

任何人都知道在哪里可以获得第三方DotNet图像处理模块,可以完成这项看似简单的任务?

2 个答案:

答案 0 :(得分:4)

这并不是那么简单,因为你需要一个3D转换,它比简单的2D转换(如旋转,缩放或剪切)更复杂,计算成本更高。为了让您了解数学上的差异,2D变换需要2乘2矩阵,而投影变换(比其他3D变换更复杂)需要4乘4矩阵......

你需要的是一些3D渲染引擎,你可以在其中绘制多边形(在透视图中),并用纹理(如地毯)覆盖它们。对于.Net 2.0,我建议使用SlimDX,它是DirectX的一个端口,允许你渲染多边形,但有一些学习曲线。如果您使用的是WPF(.Net 3.0及更高版本),则内置的3D画布允许您以透视方式绘制纹理多边形。对于您的目的,这可能比SlimDX更容易/更好学习。我确信有一种方法可以将3D画布的输出重定向到jpeg ...

如果您不需要很好的性能并且限制纹理的方向(例如,始终是水平地板或始终是垂直墙),您可以大大简化问题。如果是这样,您可以使用.Net 2.0中的简单绘图循环自行渲染。

答案 1 :(得分:3)

如果你只想要一个普通的地板,你的代码就像这样。警告:获得所需的结果将需要一些重要的时间和精确,特别是如果你不太了解数学。但另一方面,玩这种类型的代码总是很有趣......(:

在下面找到一些示例图片。

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);
    }

}
}

This is a sample output using one of Windows 7 sample images.

Another example using another Windows 7 sample picture.