Отображение 3d-модели в виде объекта 3D mesh с использованием 3D-графики wpf
Я работаю на платформе C# .Net
с 3D-графикой Wpf
.
Вот поток кода следующий:
Private void display3DView ()
{
while(loop_run)
{
using ( DepthImageFrame depthFrame = sensor.DepthStream.OpenNextFrame(1000))
{
if (depthFrame == null) continue;
Point3DCollection PointCloud ;
depthFrame.CopyDepthImagePixelDataTo(this.depthImagePixels);
float[,] ImageArray = new float[320, 240];
short [ ,] depth = new short[240,320];
for (int i = 0; i < 240; i++)
{
for (int j = 0; j <320; j++)
{
depth[i,j]= depthImagePixels[j+i *320].Depth;
ImageArray[i,j] =(float)depth[i,j]/(float)1000;
}
}
PointCloud =Calculate_PointCloud(ImageArray);
viewModel(PointCloud);
}
}
}</i>
2) я рассчитал 3D точки с параметрами камеры и данными глубины камеры Kinect
Private Point3DCollection Calculate_PointCloud (float [,] ImageArray) {
Point3DCollection PointCloud = new Point3DCollection();
float x_coodinate;``
float y_coordinate;
float z_coordinate;
float thresholdvalue = 2.0f;
for (int i = 0; i < 239; ++i)
{
for (int j = 0; j < 319; ++j)
{
if (Math.Abs(ImageArray[i, j] - ImageArray[i, j + 1]) < thresholdvalue && Math.Abs(ImageArray[i, j] - ImageArray[i + 1, j]) < thresholdvalue && Math.Abs(ImageArray[i, j + 1] - ImageArray[i + 1, j]) < thresholdvalue)
{
z_coordinate = ImageArray[i, j];
x_coodinate = ((j - this.PrincipalPointX) * z_coordinate) / FocalLengthX;
y_coordinate = ((i - this.PrincipalPointY) * z_coordinate) / FocalLengthY;
Point3D point1 = new Point3D(x_coodinate, y_coordinate, z_coordinate);
PointCloud.Add(point1);
z_coordinate = ImageArray[i, j + 1];
x_coodinate = (((j + 1) - this.PrincipalPointX) * z_coordinate) / FocalLengthX;
y_coordinate = ((i - this.PrincipalPointY) * z_coordinate) / FocalLengthY;
Point3D point2 = new Point3D(x_coodinate, y_coordinate, z_coordinate);
PointCloud.Add(point2);
z_coordinate = ImageArray[i + 1, j];
x_coodinate = ((j - this.PrincipalPointX) * z_coordinate) / FocalLengthX;
y_coordinate = (((i + 1) - this.PrincipalPointY) * z_coordinate) / FocalLengthY;
Point3D point3 = new Point3D(x_coodinate, y_coordinate, z_coordinate);
PointCloud.Add(point3);
}
}
}
return PointCloud;
}</i>
3) Здесь я преобразуется в набор треугольников с нормальной информацией о каждой 3D-точке и передает эти треугольники объекту 3D mesh и визуализирует объект 3d mesh с помощью элемента управления viewport3D
Private void viewModel(Point3DCollection points)
{
DirectionalLight DirLight1 = new DirectionalLight();
DirLight1.Color = Colors.White;
DirLight1.Direction = new Vector3D(1, 1, 1);
PerspectiveCamera Camera1 = new PerspectiveCamera();
Camera1.FarPlaneDistance = 8000;
Camera1.NearPlaneDistance = 100;
Camera1.FieldOfView = 10;
Camera1.Position = new Point3D(0, 0, 1);
Camera1.LookDirection = new Vector3D(-1, -1, -1);
Camera1.UpDirection = new Vector3D(0, 1, 0);
bool combinedvertices = true;
TriangleModel Triatomesh = new TriangleModel();
MeshGeometry3D tmesh = new MeshGeometry3D();
GeometryModel3D msheet = new GeometryModel3D();
Model3DGroup modelGroup = new Model3DGroup();
ModelVisual3D modelsVisual = new ModelVisual3D();
Viewport3D myViewport = new Viewport3D();
for(int i =0; i<points.Count; i+=3)
{
Triatomesh.addTriangleToMesh(points[i],points[i + 1], points[i + 2], tmesh, combinedvertices);
}
msheet.Geometry = tmesh;
msheet.Material = new DiffuseMaterial(new SolidColorBrush(Colors.White));
modelGroup.Children.Add(msheet);
modelGroup.Children.Add(DirLight1);
modelsVisual.Content = modelGroup;
myViewport.IsHitTestVisible = false;
myViewport.Camera = Camera1;
myViewport.Children.Add(modelsVisual);
canvas1.Children.Add(myViewport);
myViewport.Height = canvas1.Height;
myViewport.Width = canvas1.Width;
Canvas.SetTop(myViewport, 0);
Canvas.SetLeft(myViewport, 0);
} </i>
4) Вот функция, которая берет три 3D-точки и добавляет к 3D-сетке объект в виде треугольника, вычисляя нормаль к каждой 3D-точке
public void addTriangleToMesh(Point3D p0, Point3D p1, Point3D p2,
MeshGeometry3D mesh, bool combine_vertices)
{
Vector3D normal = CalculateNormal(p0, p1, p2);
if (combine_vertices)
{
addPointCombined(p0, mesh, normal);
addPointCombined(p1, mesh, normal);
addPointCombined(p2, mesh, normal);
}
else
{
mesh.Positions.Add(p0);
mesh.Positions.Add(p1);
mesh.Positions.Add(p2);
//mesh.TriangleIndices.Add(mesh.TriangleIndices.Count);
// mesh.TriangleIndices.Add(mesh.TriangleIndices.Count);
// mesh.TriangleIndices.Add(mesh.TriangleIndices.Count);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
}
}
public Vector3D CalculateNormal(Point3D P0, Point3D P1, Point3D P2) //static
{
Vector3D v0 = new Vector3D(P1.X - P0.X, P1.Y - P0.Y, P1.Z - P0.Z);
Vector3D v1 = new Vector3D(P2.X - P1.X, P2.Y - P1.Y, P2.Z - P1.Z);
return Vector3D.CrossProduct(v0, v1);
}
public void addPointCombined(Point3D point, MeshGeometry3D mesh, Vector3D normal)
{
bool found = false;
int i = 0;
foreach (Point3D p in mesh.Positions)
{
if (p.Equals(point))
{
found = true;
mesh.TriangleIndices.Add(i);
mesh.Positions.Add(point);
mesh.Normals.Add(normal);
break;
}
i++;
}
if (!found)
{
mesh.Positions.Add(point);
mesh.TriangleIndices.Add(mesh.TriangleIndices.Count);
mesh.Normals.Add(normal);
}
}
5) Вот мой XAML-код
<Window x:Class="PointCloud3DView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="PointCloud" Height="653" Width="993" Background="Black" Loaded="Window_Loaded">
<Grid Height="1130" Width="1626">
<Canvas Height="611" HorizontalAlignment="Left" Name="canvas1" VerticalAlignment="Top"
Width= " 967 "Background=" Черный" />
</Grid>
Проблема в том, что я не могу получить отображаемую 3D-модель на экране Wpf.Пожалуйста, может ли кто - нибудь пройти через весь код ? и заставить меня понять, где я ошибаюсь? а также предложите мне внести коррективы. Заранее спасибо
1 ответ:
Я экспериментирую с WPF 3D уже несколько недель и научился некоторым жестким уменьшениям:) У меня нет времени проверять и пробовать весь код сейчас, так как я в работе. Однако я бы попробовал три вещи:
Я не уверен в направлении вашей камеры. Он находится в (0,1,0), глядя с помощью вектора (-1, -1, -1), это означает, что он сосредоточен на центральном точка (-1,0,-1). И это довольно странно... Попробуйте расположить камеру далее (в зависимости от масштаба вашей модели) типа (0,10,0) или даже далее и сфокусируйте его на (0,0,0) или где-нибудь в центральной точке вашего модель такова:
Camera1.Position = new Point3D(0, 10, 0);
Camera1.LookDirection = новый point3d и выполняет обратное преобразование(0,0,0) - Камеру1.Положение;Также удалите направленный свет (так как он использует нормали, и если они неверны, ничего не будет показано) и попробуйте вместо этого рассеянный свет. А твой направленная молния имеет прямо противоположный вектор вашему взгляду направление (-1, -1,-1) и (1,1,1).
Попробуйте поменять местами порядок точек в индексах треугольника (WPF отображает только одну сторону сетки, поэтому модель может быть там, но внутри / снаружи) - вместо 0,1,2 попробуй 0,2,1;
Если ничего не поможет, я попробую ваш код, как только вернусь домой.
/отредактировано позже / Я попробовал ваш код на простом треугольнике и переписал его в соответствии с моими советами, и это сработало. Есть несколько комментариев и два совета, как немного очистить ваш код:)
private void viewModel(Point3DCollection points) { DirectionalLight DirLight1 = new DirectionalLight(); DirLight1.Color = Colors.White; DirLight1.Direction = new Vector3D(1, 1, 1); PerspectiveCamera Camera1 = new PerspectiveCamera(); Camera1.FarPlaneDistance = 8000; //Camera1.NearPlaneDistance = 100; //close object will not be displayed with this option Camera1.FieldOfView = 10; //Camera1.Position = new Point3D(0, 0, 1); //Camera1.LookDirection = new Vector3D(-1, -1, -1); Camera1.Position = new Point3D(0, 0, 10); Camera1.LookDirection = new Point3D(0, 0, 0) - Camera1.Position; //focus camera on real center of your model (0,0,0) in this case Camera1.UpDirection = new Vector3D(0, 1, 0); //you can use constructor to create Camera instead of assigning its properties like: //PerspectiveCamera Camera1 = new PerspectiveCamera(new Point3D(0,0,10), new Vector3D(0,0,-1), new Vector3D(0,1,0), 10); bool combinedvertices = true; TriangleModel Triatomesh = new TriangleModel(); MeshGeometry3D tmesh = new MeshGeometry3D(); GeometryModel3D msheet = new GeometryModel3D(); Model3DGroup modelGroup = new Model3DGroup(); ModelVisual3D modelsVisual = new ModelVisual3D(); Viewport3D myViewport = new Viewport3D(); for (int i = 0; i < points.Count; i += 3) { Triatomesh.addTriangleToMesh(points[i + 2], points[i + 1], points[i], tmesh, combinedvertices); //I did swap order of vertexes you may try both options with your model } msheet.Geometry = tmesh; msheet.Material = new DiffuseMaterial(new SolidColorBrush(Colors.White)); //you can use constructor to create GeometryModel3D instead of assigning its properties like: //msheet = new GeometryModel3D(tmesh, new DiffuseMaterial(new SolidColorBrush(Colors.White))); modelGroup.Children.Add(msheet); //use AMbientLIght instead of directional modelGroup.Children.Add(new AmbientLight(Colors.White)); modelsVisual.Content = modelGroup; myViewport.IsHitTestVisible = false; myViewport.Camera = Camera1; myViewport.Children.Add(modelsVisual); canvas1.Children.Add(myViewport); myViewport.Height = canvas1.Height; myViewport.Width = canvas1.Width; Canvas.SetTop(myViewport, 0); Canvas.SetLeft(myViewport, 0); }
И Points3DCollection, который я использовал в качестве параметр (вместо ввода Kinect):
Point3DCollection points = new Point3DCollection(); points.Add(new Point3D(0.5, 0, 0.5)); points.Add(new Point3D(0.5, -0.5, -0.5)); points.Add(new Point3D(-0.5, -0.1, -0.5)); viewModel(points);