在球体上映射图像并返回未展开的等距矩形投影

时间:2010-11-16 21:18:57

标签: c# graphics

我正在尝试完成以下任务:

想象一下,画布的宽高比为2:1。我想在这个画布上放置矩形图像(.. x,y笛卡尔坐标),当我将图像移动到远离画布中心(0,0)时,我想让图像失真,好像它正在滑动超过假想球体的法线。

我认为逻辑是将图像的(x,y)2D笛卡尔坐标转换为相应的纬度,经度球面坐标,然后将等距矩形投影应用于球体并返回具有相应失真的图像(如如果它是在3D建模包中打开的uvs。)

有一个应用正是这样做的,称为“HDR Light Studio”,它可以生成用于3D场景照明的equirectangular(lat / long)HDR图像。 如果您查看他们网站上的视频,您就会明白我想要实现的图像失真类型。

如果可以的话,请给我一些帮助。 我在网上搜索了所有信息,但没有运气。

提前致谢

1 个答案:

答案 0 :(得分:0)

如果我正确理解了这个问题,你应该能够通过创建一个球体的一部分并将图像用作纹理来达到同样的效果。您可以旋转球体而不是移动图片。

主窗口的代码和XAML:

namespace WpfBalls
{
   public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            Ball ball = new Ball();
            ball.ImageSource = "YourPictureHere.jpg"; //path to the picture
            visualModel.Children.Add(ball);
        }
    }
}

<Window x:Class="WpfBalls.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfBalls"
    Title="3D WpfBalls" Height="400" Width="600">
    <Grid>
        <Grid.Background>
            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                <LinearGradientBrush.GradientStops>
                    <GradientStop Color="White" Offset="0"/>
                    <GradientStop Color="White" Offset="1"/>
                </LinearGradientBrush.GradientStops>
            </LinearGradientBrush>
        </Grid.Background>
        <Viewport3D x:Name="viewPort" Grid.Column="0" Grid.Row="0" ClipToBounds="False">
            <Viewport3D.Camera>
                <PerspectiveCamera x:Name="camera" Position="0,0,-8" 
                                   UpDirection="0,1,0" LookDirection="0,0,1" 
                                   FieldOfView="25" NearPlaneDistance="0.125"/>
            </Viewport3D.Camera>
            <Viewport3D.Children>
                <ModelVisual3D>
                    <ModelVisual3D.Content>
                        <AmbientLight Color="White"  />
                    </ModelVisual3D.Content>
                </ModelVisual3D>
                <ModelVisual3D x:Name="visualModel">

                        <ModelVisual3D.Transform>
                        <Transform3DGroup>
                            <RotateTransform3D>
                                <RotateTransform3D.Rotation>
                                    <AxisAngleRotation3D  x:Name="rotationY" 
                                                          Angle="{Binding ElementName=sliderY,Path=Value}" Axis="0,1,0" />
                                </RotateTransform3D.Rotation>
                            </RotateTransform3D>
                            <RotateTransform3D>
                                <RotateTransform3D.Rotation>
                                    <AxisAngleRotation3D  x:Name="rotationX" 
                                                          Angle="{Binding ElementName=sliderX,Path=Value}" Axis="1,0,0" />
                                </RotateTransform3D.Rotation>
                            </RotateTransform3D>
                        </Transform3DGroup>
                    </ModelVisual3D.Transform>

                </ModelVisual3D>
            </Viewport3D.Children>
        </Viewport3D>
        <Slider Height="23" HorizontalAlignment="Left" Margin="25,23,0,0" Name="sliderX" Minimum="-70" Maximum="70" Value="0"  VerticalAlignment="Top" Width="100" />
        <Slider Height="86" HorizontalAlignment="Left" Margin="25,52,0,0" Name="sliderY" Minimum="110" Maximum="250" Value="180" VerticalAlignment="Top" Width="27" Orientation="Vertical" />
    </Grid>
</Window>

Ball类代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Media3D;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace WpfBalls
{
    public class Ball : ModelVisual3D
    {
        public Ball()
        {
            this.Content = new GeometryModel3D();
            (this.Content as GeometryModel3D).Geometry = Tessellate();

        }

        static double DegToRad(double degrees)
        {
            return (degrees / 180.0) * Math.PI;
        }

        internal Point3D GetPosition(double t, double y)
        {
            double r = Math.Sqrt(1 - y * y);
            double x = r * Math.Cos(t);
            double z = r * Math.Sin(t);

            return new Point3D(x, y, z);
        }

        private Vector3D GetNormal(double t, double y)
        {
            return (Vector3D)GetPosition(t, y);
        }

        private Point GetTextureCoordinate(double t, double y)
        {
            Matrix TYtoUV = new Matrix();
            TYtoUV.Scale(1 / (2 * Math.PI), -0.5);

            Point p = new Point(t, y);
            p = p * TYtoUV;

            return p;
        }

        public string ImageSource
        {
            set {

                DiffuseMaterial dm = new DiffuseMaterial();
                ImageSource imSrc = new 
                    BitmapImage( new Uri( value, UriKind.RelativeOrAbsolute ) );
                dm.Brush = new ImageBrush( imSrc );


                (this.Content as GeometryModel3D).Material = dm; 
            }
        }

        public Point3D Offset
        {
            set {
                this.Transform = new 
                    TranslateTransform3D(value.X, value.Y, value.Z);
            }
        }

        internal Geometry3D Tessellate()
        {
            int tDiv =750;
            int yDiv = 750;
            double maxTheta = DegToRad(360);
            double minY = -1.0;
            double maxY = 1.0;

            double dt = maxTheta / tDiv;
            double dy = (maxY - minY) / yDiv;

            MeshGeometry3D mesh = new MeshGeometry3D();

            for (int yi = 0; yi <= yDiv; yi++)
            {
                double y = minY + yi * dy;

                for (int ti = 0; ti <= tDiv; ti++)
                {
                    double t = ti * dt;
                    var p = GetPosition(t, y);
                    if (p.Z > 0 && p.X > -.5 && p.X < .5 && p.Y > -.5 && p.Y < .5)
                    {
                    mesh.Positions.Add(p);
                    mesh.Normals.Add(GetNormal(t, y));
                    mesh.TextureCoordinates.Add(GetTextureCoordinate(t, y));
                    }

                }
            }

            for (int yi = 0; yi < yDiv; yi++)
            {
                for (int ti = 0; ti < tDiv; ti++)
                {
                    int x0 = ti;
                    int x1 = (ti + 1);
                    int y0 = yi * (tDiv + 1);
                    int y1 = (yi + 1) * (tDiv + 1);

                    mesh.TriangleIndices.Add(x0 + y0);
                    mesh.TriangleIndices.Add(x0 + y1);
                    mesh.TriangleIndices.Add(x1 + y0);

                    mesh.TriangleIndices.Add(x1 + y0);
                    mesh.TriangleIndices.Add(x0 + y1);
                    mesh.TriangleIndices.Add(x1 + y1);
                }
            }

            mesh.Freeze();
            return mesh;
        }
    }
}

代码基于此示例: http://www.codegod.de/WebAppCodeGod/wpf-3d-animations-and-textures-AID439.aspx