আমি আশা করছি যে আমার 5 বছরের মতো কেউ আমাকে এটি ব্যাখ্যা করতে পারে কারণ আমি কয়েক ঘন্টা ধরে এই নিয়ে লড়াই করে চলেছি এবং আমি কী ভুল করছি তা কেবল বুঝতে পারি না।
আমি Camera
আমার 2.5 ডি গেমের জন্য ক্লাস লিখেছি । উদ্দেশ্যটি হ'ল এইভাবে বিশ্ব এবং পর্দার স্থানগুলিকে সমর্থন করা:
ক্যামেরাটি ডানদিকে কালো জিনিস। + জেড অক্ষটি সেই চিত্রটিতে উপরের দিকে, জেডটি নীচের দিকে চলেছে। আপনি দেখতে পাচ্ছেন, উভয় বিশ্বের স্থান এবং পর্দার স্থানগুলির শীর্ষ-বামে (0, 0) রয়েছে।
আমার ক্যামেরা প্রত্যাশা অনুযায়ী কাজ করছে তা প্রমাণ করার জন্য আমি কয়েকটি ইউনিট পরীক্ষা লিখতে শুরু করেছিলাম এবং এখানেই জিনিসগুলি ... অদ্ভুত হতে শুরু করে। আমার পরীক্ষাগুলি বিশ্ব, দেখুন এবং স্ক্রিন স্পেসে স্থানাঙ্কগুলি প্লট করে। অবশেষে আমি চিত্রের তুলনাটি এগুলি দৃ to় করার জন্য ব্যবহার করব যে তারা সঠিক, তবে আপাতত আমার পরীক্ষাটি ফলাফলটি প্রদর্শন করে।
রেন্ডার যুক্তি ব্যবহার Camera.ViewMatrix
করে স্থান দেখার Camera.WorldPointToScreen
জন্য বিশ্ব স্থানকে রূপান্তর করতে এবং বিশ্ব স্থানকে স্ক্রিন স্পেসে রূপান্তর করতে।
এখানে একটি উদাহরণ পরীক্ষা:
[Fact]
public void foo()
{
var camera = new Camera(new Viewport(0, 0, 250, 100));
DrawingVisual worldRender;
DrawingVisual viewRender;
DrawingVisual screenRender;
this.Render(camera, out worldRender, out viewRender, out screenRender, new Vector3(30, 0, 0), new Vector3(30, 40, 0));
this.ShowRenders(camera, worldRender, viewRender, screenRender);
}
এবং এই টেস্টটি চালানোর সময় কী পপ আপ হয় তা এখানে:
বিশ্ব স্পেস ঠিক আছে, যদিও আমি সন্দেহ করি z অক্ষটি দর্শকের দিকে না গিয়ে পর্দায় চলে যাচ্ছে।
স্থান দেখার স্থানটি আমাকে সম্পূর্ণ বিভ্রান্ত করেছে। আমি আশা করছিলাম ক্যামেরাটি উপরে বসে থাকবে (0, 0) এবং দৃশ্যের কেন্দ্রের দিকে তাকাবে। পরিবর্তে, z অক্ষটি চারপাশে ভুল পথ বলে মনে হচ্ছে এবং ক্যামেরাটি আমার প্রত্যাশা অনুযায়ী বিপরীত কোণে অবস্থান করছে!
আমার সন্দেহ হয় স্ক্রিন স্পেস পুরোপুরি আরেকটি জিনিস হয়ে যাবে, তবে আমার Camera
ক্লাসে আমি কী ভুল করছি তা কি কেউ ব্যাখ্যা করতে পারেন ?
হালনাগাদ
আমি যেমনটি প্রত্যাশা করি তেমনভাবে দৃষ্টি আকর্ষণীয় হয়ে উঠার ক্ষেত্রে আমি কিছুটা অগ্রগতি করেছি, তবে কেবল স্বজ্ঞাততার মাধ্যমে: আমি কী করছি তার প্রকৃত উপলব্ধি নেই। যে কোনও আলোকিতকরণ প্রশংসিত হবে।
আমি বুঝতে পারি যে আমার দেখার স্থানটি আমার প্রত্যাশার তুলনায় উল্লম্ব এবং অনুভূমিকভাবে উভয়ই উল্টানো হয়েছিল, তাই আমি আমার ভিউ ম্যাট্রিক্সকে সেই অনুযায়ী স্কেলে পরিবর্তন করেছি:
this.viewMatrix = Matrix.CreateLookAt(this.location, this.target, this.up) *
Matrix.CreateScale(this.zoom, this.zoom, 1) *
Matrix.CreateScale(-1, -1, 1);
আমি দুটি CreateScale
কল একত্রিত করতে পারি , তবে স্বচ্ছতার জন্য তাদের আলাদা রেখেছি । আবার, কেন এটি প্রয়োজনীয় তা আমার কোনও ধারণা নেই তবে এটি আমার দেখার স্থানটি স্থির করেছে:
তবে এখন আমার স্ক্রিনের স্থানটি উল্লম্বভাবে উল্টানো দরকার, তাই আমি সেই অনুযায়ী আমার প্রজেকশন ম্যাট্রিক্সটি সংশোধন করেছি:
this.projectionMatrix = Matrix.CreatePerspectiveFieldOfView(0.7853982f, viewport.AspectRatio, 1, 2)
* Matrix.CreateScale(1, -1, 1);
এবং এটি আমার প্রথম প্রয়াস থেকে যা প্রত্যাশা করেছিলাম তার ফলাফল:
আমি সমস্ত কিছু সেখানে কাজ করে কি না তা নিশ্চিত করার জন্য কেবল Camera
একটি মাধ্যমে স্প্রিট রেন্ডার ব্যবহার করার চেষ্টা করেছি SpriteBatch
এবং এটিও করে।
তবে প্রশ্নটি রয়ে গেছে: স্থানটি আমার যেভাবে প্রত্যাশা করা হচ্ছে তা স্থিতিশীল করার জন্য কেন আমি অক্ষের এই সমস্ত ফ্লিপিং করা দরকার?
আপডেট 2
আমি তখন থেকে আমার পরীক্ষার স্যুটে আমার রেন্ডারিং লজিকটিকে আরও উন্নত করেছি যাতে এটি জ্যামিতিগুলিকে সমর্থন করে এবং যাতে লাইনগুলি ক্যামেরা থেকে আরও দূরে হালকা হয়। অপটিক্যাল ভ্রমগুলি এড়াতে এবং নিজের কাছে আরও প্রমাণ করার জন্য আমি এটি করতে চেয়েছিলাম আমি যা ভাবছি তা আমি দেখছি।
এখানে একটি উদাহরণ:
এই ক্ষেত্রে, আমার কাছে 3 টি জ্যামিতি রয়েছে: একটি ঘনক্ষেত্র, একটি গোলক এবং কিউবের উপরের মুখের উপরে একটি পললাইন। লক্ষ করুন কীভাবে রেখাগুলির অন্ধকার এবং হালকা করা জ্যামিতির সেই অংশগুলিকে সঠিকভাবে ক্যামেরার নিকটে সনাক্ত করতে পারে।
আমি যে নেতিবাচক স্কেলিংটি রেখেছিলাম তা যদি আমি সরিয়ে ফেলি তবে আমি দেখতে পাচ্ছি:
সুতরাং আপনি দেখতে পাচ্ছেন যে আমি এখনও একই নৌকোতে রয়েছি - জিনিসগুলি সঠিকভাবে প্রদর্শিত হওয়ার জন্য আমার এখনও ম্যাট্রিক্সে এই উল্লম্ব এবং অনুভূমিক উল্টানো দরকার।
লোকেদের সাথে খেলতে তিরস্কার করার স্বার্থে, উপরের উত্পন্ন করার জন্য প্রয়োজনীয় কোডটি এখানে। আপনি যদি পরীক্ষার জোতা দিয়ে চালাতে চান তবে কেবল xunit প্যাকেজটি ইনস্টল করুন:
ক্যামেরা.এস :
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;
public sealed class Camera
{
private readonly Viewport viewport;
private readonly Matrix projectionMatrix;
private Matrix? viewMatrix;
private Vector3 location;
private Vector3 target;
private Vector3 up;
private float zoom;
public Camera(Viewport viewport)
{
this.viewport = viewport;
// for an explanation of the negative scaling, see: http://gamedev.stackexchange.com/questions/63409/
this.projectionMatrix = Matrix.CreatePerspectiveFieldOfView(0.7853982f, viewport.AspectRatio, 1, 2)
* Matrix.CreateScale(1, -1, 1);
// defaults
this.location = new Vector3(this.viewport.Width / 2, this.viewport.Height, 100);
this.target = new Vector3(this.viewport.Width / 2, this.viewport.Height / 2, 0);
this.up = new Vector3(0, 0, 1);
this.zoom = 1;
}
public Viewport Viewport
{
get { return this.viewport; }
}
public Vector3 Location
{
get { return this.location; }
set
{
this.location = value;
this.viewMatrix = null;
}
}
public Vector3 Target
{
get { return this.target; }
set
{
this.target = value;
this.viewMatrix = null;
}
}
public Vector3 Up
{
get { return this.up; }
set
{
this.up = value;
this.viewMatrix = null;
}
}
public float Zoom
{
get { return this.zoom; }
set
{
this.zoom = value;
this.viewMatrix = null;
}
}
public Matrix ProjectionMatrix
{
get { return this.projectionMatrix; }
}
public Matrix ViewMatrix
{
get
{
if (this.viewMatrix == null)
{
// for an explanation of the negative scaling, see: http://gamedev.stackexchange.com/questions/63409/
this.viewMatrix = Matrix.CreateLookAt(this.location, this.target, this.up) *
Matrix.CreateScale(this.zoom) *
Matrix.CreateScale(-1, -1, 1);
}
return this.viewMatrix.Value;
}
}
public Vector2 WorldPointToScreen(Vector3 point)
{
var result = viewport.Project(point, this.ProjectionMatrix, this.ViewMatrix, Matrix.Identity);
return new Vector2(result.X, result.Y);
}
public void WorldPointsToScreen(Vector3[] points, Vector2[] destination)
{
Debug.Assert(points != null);
Debug.Assert(destination != null);
Debug.Assert(points.Length == destination.Length);
for (var i = 0; i < points.Length; ++i)
{
destination[i] = this.WorldPointToScreen(points[i]);
}
}
}
ক্যামেরাফাইচার.সি :
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Xunit;
using XNA = Microsoft.Xna.Framework;
public sealed class CameraFixture
{
[Fact]
public void foo()
{
var camera = new Camera(new Viewport(0, 0, 250, 100));
DrawingVisual worldRender;
DrawingVisual viewRender;
DrawingVisual screenRender;
this.Render(
camera,
out worldRender,
out viewRender,
out screenRender,
new Sphere(30, 15) { WorldMatrix = XNA.Matrix.CreateTranslation(155, 50, 0) },
new Cube(30) { WorldMatrix = XNA.Matrix.CreateTranslation(75, 60, 15) },
new PolyLine(new XNA.Vector3(0, 0, 0), new XNA.Vector3(10, 10, 0), new XNA.Vector3(20, 0, 0), new XNA.Vector3(0, 0, 0)) { WorldMatrix = XNA.Matrix.CreateTranslation(65, 55, 30) });
this.ShowRenders(worldRender, viewRender, screenRender);
}
#region Supporting Fields
private static readonly Pen xAxisPen = new Pen(Brushes.Red, 2);
private static readonly Pen yAxisPen = new Pen(Brushes.Green, 2);
private static readonly Pen zAxisPen = new Pen(Brushes.Blue, 2);
private static readonly Pen viewportPen = new Pen(Brushes.Gray, 1);
private static readonly Pen nonScreenSpacePen = new Pen(Brushes.Black, 0.5);
private static readonly Color geometryBaseColor = Colors.Black;
#endregion
#region Supporting Methods
private void Render(Camera camera, out DrawingVisual worldRender, out DrawingVisual viewRender, out DrawingVisual screenRender, params Geometry[] geometries)
{
var worldDrawingVisual = new DrawingVisual();
var viewDrawingVisual = new DrawingVisual();
var screenDrawingVisual = new DrawingVisual();
const int axisLength = 15;
using (var worldDrawingContext = worldDrawingVisual.RenderOpen())
using (var viewDrawingContext = viewDrawingVisual.RenderOpen())
using (var screenDrawingContext = screenDrawingVisual.RenderOpen())
{
// draw lines around the camera's viewport
var viewportBounds = camera.Viewport.Bounds;
var viewportLines = new Tuple<int, int, int, int>[]
{
Tuple.Create(viewportBounds.Left, viewportBounds.Bottom, viewportBounds.Left, viewportBounds.Top),
Tuple.Create(viewportBounds.Left, viewportBounds.Top, viewportBounds.Right, viewportBounds.Top),
Tuple.Create(viewportBounds.Right, viewportBounds.Top, viewportBounds.Right, viewportBounds.Bottom),
Tuple.Create(viewportBounds.Right, viewportBounds.Bottom, viewportBounds.Left, viewportBounds.Bottom)
};
foreach (var viewportLine in viewportLines)
{
var viewStart = XNA.Vector3.Transform(new XNA.Vector3(viewportLine.Item1, viewportLine.Item2, 0), camera.ViewMatrix);
var viewEnd = XNA.Vector3.Transform(new XNA.Vector3(viewportLine.Item3, viewportLine.Item4, 0), camera.ViewMatrix);
var screenStart = camera.WorldPointToScreen(new XNA.Vector3(viewportLine.Item1, viewportLine.Item2, 0));
var screenEnd = camera.WorldPointToScreen(new XNA.Vector3(viewportLine.Item3, viewportLine.Item4, 0));
worldDrawingContext.DrawLine(viewportPen, new Point(viewportLine.Item1, viewportLine.Item2), new Point(viewportLine.Item3, viewportLine.Item4));
viewDrawingContext.DrawLine(viewportPen, new Point(viewStart.X, viewStart.Y), new Point(viewEnd.X, viewEnd.Y));
screenDrawingContext.DrawLine(viewportPen, new Point(screenStart.X, screenStart.Y), new Point(screenEnd.X, screenEnd.Y));
}
// draw axes
var axisLines = new Tuple<int, int, int, int, int, int, Pen>[]
{
Tuple.Create(0, 0, 0, axisLength, 0, 0, xAxisPen),
Tuple.Create(0, 0, 0, 0, axisLength, 0, yAxisPen),
Tuple.Create(0, 0, 0, 0, 0, axisLength, zAxisPen)
};
foreach (var axisLine in axisLines)
{
var viewStart = XNA.Vector3.Transform(new XNA.Vector3(axisLine.Item1, axisLine.Item2, axisLine.Item3), camera.ViewMatrix);
var viewEnd = XNA.Vector3.Transform(new XNA.Vector3(axisLine.Item4, axisLine.Item5, axisLine.Item6), camera.ViewMatrix);
var screenStart = camera.WorldPointToScreen(new XNA.Vector3(axisLine.Item1, axisLine.Item2, axisLine.Item3));
var screenEnd = camera.WorldPointToScreen(new XNA.Vector3(axisLine.Item4, axisLine.Item5, axisLine.Item6));
worldDrawingContext.DrawLine(axisLine.Item7, new Point(axisLine.Item1, axisLine.Item2), new Point(axisLine.Item4, axisLine.Item5));
viewDrawingContext.DrawLine(axisLine.Item7, new Point(viewStart.X, viewStart.Y), new Point(viewEnd.X, viewEnd.Y));
screenDrawingContext.DrawLine(axisLine.Item7, new Point(screenStart.X, screenStart.Y), new Point(screenEnd.X, screenEnd.Y));
}
// for all points in all geometries to be rendered, find the closest and furthest away from the camera so we can lighten lines that are further away
var distancesToAllGeometrySections = from geometry in geometries
let geometryViewMatrix = geometry.WorldMatrix * camera.ViewMatrix
from section in geometry.Sections
from point in new XNA.Vector3[] { section.Item1, section.Item2 }
let viewPoint = XNA.Vector3.Transform(point, geometryViewMatrix)
select viewPoint.Length();
var furthestDistance = distancesToAllGeometrySections.Max();
var closestDistance = distancesToAllGeometrySections.Min();
var deltaDistance = Math.Max(0.000001f, furthestDistance - closestDistance);
// draw each geometry
for (var i = 0; i < geometries.Length; ++i)
{
var geometry = geometries[i];
// there's probably a more correct name for this, but basically this gets the geometry relative to the camera so we can check how far away each point is from the camera
var geometryViewMatrix = geometry.WorldMatrix * camera.ViewMatrix;
// we order roughly by those sections furthest from the camera to those closest, so that the closer ones "overwrite" the ones further away
var orderedSections = from section in geometry.Sections
let startPointRelativeToCamera = XNA.Vector3.Transform(section.Item1, geometryViewMatrix)
let endPointRelativeToCamera = XNA.Vector3.Transform(section.Item2, geometryViewMatrix)
let startPointDistance = startPointRelativeToCamera.Length()
let endPointDistance = endPointRelativeToCamera.Length()
orderby (startPointDistance + endPointDistance) descending
select new { Section = section, DistanceToStart = startPointDistance, DistanceToEnd = endPointDistance };
foreach (var orderedSection in orderedSections)
{
var start = XNA.Vector3.Transform(orderedSection.Section.Item1, geometry.WorldMatrix);
var end = XNA.Vector3.Transform(orderedSection.Section.Item2, geometry.WorldMatrix);
var viewStart = XNA.Vector3.Transform(start, camera.ViewMatrix);
var viewEnd = XNA.Vector3.Transform(end, camera.ViewMatrix);
worldDrawingContext.DrawLine(nonScreenSpacePen, new Point(start.X, start.Y), new Point(end.X, end.Y));
viewDrawingContext.DrawLine(nonScreenSpacePen, new Point(viewStart.X, viewStart.Y), new Point(viewEnd.X, viewEnd.Y));
// screen rendering is more complicated purely because I wanted geometry to fade the further away it is from the camera
// otherwise, it's very hard to tell whether the rendering is actually correct or not
var startDistanceRatio = (orderedSection.DistanceToStart - closestDistance) / deltaDistance;
var endDistanceRatio = (orderedSection.DistanceToEnd - closestDistance) / deltaDistance;
// lerp towards white based on distance from camera, but only to a maximum of 90%
var startColor = Lerp(geometryBaseColor, Colors.White, startDistanceRatio * 0.9f);
var endColor = Lerp(geometryBaseColor, Colors.White, endDistanceRatio * 0.9f);
var screenStart = camera.WorldPointToScreen(start);
var screenEnd = camera.WorldPointToScreen(end);
var brush = new LinearGradientBrush
{
StartPoint = new Point(screenStart.X, screenStart.Y),
EndPoint = new Point(screenEnd.X, screenEnd.Y),
MappingMode = BrushMappingMode.Absolute
};
brush.GradientStops.Add(new GradientStop(startColor, 0));
brush.GradientStops.Add(new GradientStop(endColor, 1));
var pen = new Pen(brush, 1);
brush.Freeze();
pen.Freeze();
screenDrawingContext.DrawLine(pen, new Point(screenStart.X, screenStart.Y), new Point(screenEnd.X, screenEnd.Y));
}
}
}
worldRender = worldDrawingVisual;
viewRender = viewDrawingVisual;
screenRender = screenDrawingVisual;
}
private static float Lerp(float start, float end, float amount)
{
var difference = end - start;
var adjusted = difference * amount;
return start + adjusted;
}
private static Color Lerp(Color color, Color to, float amount)
{
var sr = color.R;
var sg = color.G;
var sb = color.B;
var er = to.R;
var eg = to.G;
var eb = to.B;
var r = (byte)Lerp(sr, er, amount);
var g = (byte)Lerp(sg, eg, amount);
var b = (byte)Lerp(sb, eb, amount);
return Color.FromArgb(255, r, g, b);
}
private void ShowRenders(DrawingVisual worldRender, DrawingVisual viewRender, DrawingVisual screenRender)
{
var itemsControl = new ItemsControl();
itemsControl.Items.Add(new HeaderedContentControl { Header = "World", Content = new DrawingVisualHost(worldRender)});
itemsControl.Items.Add(new HeaderedContentControl { Header = "View", Content = new DrawingVisualHost(viewRender) });
itemsControl.Items.Add(new HeaderedContentControl { Header = "Screen", Content = new DrawingVisualHost(screenRender) });
var window = new Window
{
Title = "Renders",
Content = itemsControl,
ShowInTaskbar = true,
SizeToContent = SizeToContent.WidthAndHeight
};
window.ShowDialog();
}
#endregion
#region Supporting Types
// stupidly simple 3D geometry class, consisting of a series of sections that will be connected by lines
private abstract class Geometry
{
public abstract IEnumerable<Tuple<XNA.Vector3, XNA.Vector3>> Sections
{
get;
}
public XNA.Matrix WorldMatrix
{
get;
set;
}
}
private sealed class Line : Geometry
{
private readonly XNA.Vector3 magnitude;
public Line(XNA.Vector3 magnitude)
{
this.magnitude = magnitude;
}
public override IEnumerable<Tuple<XNA.Vector3, XNA.Vector3>> Sections
{
get
{
yield return Tuple.Create(XNA.Vector3.Zero, this.magnitude);
}
}
}
private sealed class PolyLine : Geometry
{
private readonly XNA.Vector3[] points;
public PolyLine(params XNA.Vector3[] points)
{
this.points = points;
}
public override IEnumerable<Tuple<XNA.Vector3, XNA.Vector3>> Sections
{
get
{
if (this.points.Length < 2)
{
yield break;
}
var end = this.points[0];
for (var i = 1; i < this.points.Length; ++i)
{
var start = end;
end = this.points[i];
yield return Tuple.Create(start, end);
}
}
}
}
private sealed class Cube : Geometry
{
private readonly float size;
public Cube(float size)
{
this.size = size;
}
public override IEnumerable<Tuple<XNA.Vector3, XNA.Vector3>> Sections
{
get
{
var halfSize = this.size / 2;
var frontBottomLeft = new XNA.Vector3(-halfSize, halfSize, -halfSize);
var frontBottomRight = new XNA.Vector3(halfSize, halfSize, -halfSize);
var frontTopLeft = new XNA.Vector3(-halfSize, halfSize, halfSize);
var frontTopRight = new XNA.Vector3(halfSize, halfSize, halfSize);
var backBottomLeft = new XNA.Vector3(-halfSize, -halfSize, -halfSize);
var backBottomRight = new XNA.Vector3(halfSize, -halfSize, -halfSize);
var backTopLeft = new XNA.Vector3(-halfSize, -halfSize, halfSize);
var backTopRight = new XNA.Vector3(halfSize, -halfSize, halfSize);
// front face
yield return Tuple.Create(frontBottomLeft, frontBottomRight);
yield return Tuple.Create(frontBottomLeft, frontTopLeft);
yield return Tuple.Create(frontTopLeft, frontTopRight);
yield return Tuple.Create(frontTopRight, frontBottomRight);
// left face
yield return Tuple.Create(frontTopLeft, backTopLeft);
yield return Tuple.Create(backTopLeft, backBottomLeft);
yield return Tuple.Create(backBottomLeft, frontBottomLeft);
// right face
yield return Tuple.Create(frontTopRight, backTopRight);
yield return Tuple.Create(backTopRight, backBottomRight);
yield return Tuple.Create(backBottomRight, frontBottomRight);
// back face
yield return Tuple.Create(backBottomLeft, backBottomRight);
yield return Tuple.Create(backTopLeft, backTopRight);
}
}
}
private sealed class Sphere : Geometry
{
private readonly float radius;
private readonly int subsections;
public Sphere(float radius, int subsections)
{
this.radius = radius;
this.subsections = subsections;
}
public override IEnumerable<Tuple<XNA.Vector3, XNA.Vector3>> Sections
{
get
{
var latitudeLines = this.subsections;
var longitudeLines = this.subsections;
// see http://stackoverflow.com/a/4082020/5380
var results = from latitudeLine in Enumerable.Range(0, latitudeLines)
from longitudeLine in Enumerable.Range(0, longitudeLines)
let latitudeRatio = latitudeLine / (float)latitudeLines
let longitudeRatio = longitudeLine / (float)longitudeLines
let nextLatitudeRatio = (latitudeLine + 1) / (float)latitudeLines
let nextLongitudeRatio = (longitudeLine + 1) / (float)longitudeLines
let z1 = Math.Cos(Math.PI * latitudeRatio)
let z2 = Math.Cos(Math.PI * nextLatitudeRatio)
let x1 = Math.Sin(Math.PI * latitudeRatio) * Math.Cos(Math.PI * 2 * longitudeRatio)
let y1 = Math.Sin(Math.PI * latitudeRatio) * Math.Sin(Math.PI * 2 * longitudeRatio)
let x2 = Math.Sin(Math.PI * nextLatitudeRatio) * Math.Cos(Math.PI * 2 * longitudeRatio)
let y2 = Math.Sin(Math.PI * nextLatitudeRatio) * Math.Sin(Math.PI * 2 * longitudeRatio)
let x3 = Math.Sin(Math.PI * latitudeRatio) * Math.Cos(Math.PI * 2 * nextLongitudeRatio)
let y3 = Math.Sin(Math.PI * latitudeRatio) * Math.Sin(Math.PI * 2 * nextLongitudeRatio)
let start = new XNA.Vector3((float)x1 * radius, (float)y1 * radius, (float)z1 * radius)
let firstEnd = new XNA.Vector3((float)x2 * radius, (float)y2 * radius, (float)z2 * radius)
let secondEnd = new XNA.Vector3((float)x3 * radius, (float)y3 * radius, (float)z1 * radius)
select new { First = Tuple.Create(start, firstEnd), Second = Tuple.Create(start, secondEnd) };
foreach (var result in results)
{
yield return result.First;
yield return result.Second;
}
}
}
}
#endregion
}
Viewport.Project
বিশ্ব ম্যাট্রিক্সের প্রয়োজন ছাড়াও । অতএব, আমি আমার এপিআইতে একটি ওয়ার্ল্ড ম্যাট্রিক্স যুক্ত করেছি। এটি হতে পারে যে আমি প্রয়োজন হলে এটি অপসারণ শেষ।