public void PolygonHitTests() { SolidMaterial redStuff = new SolidMaterial(new ColorF(1, 0, 0), 0, 0, 2); { TriangleShape facingPositiveX = new TriangleShape(new Vector3(0, 1, -1), new Vector3(0, 0, 1), new Vector3(0, -1, -1), redStuff); IntersectInfo positiveXInfo = facingPositiveX.GetClosestIntersection(new Ray(new Vector3(1, 0, 0), new Vector3(-1, 0, 0))); Assert.IsTrue(positiveXInfo.HitPosition == new Vector3(0, 0, 0)); Assert.IsTrue(positiveXInfo.HitType == IntersectionType.FrontFace); Assert.IsTrue(positiveXInfo.ClosestHitObject == facingPositiveX); Assert.IsTrue(positiveXInfo.DistanceToHit == 1); IntersectInfo negativeXInfo = facingPositiveX.GetClosestIntersection(new Ray(new Vector3(-1, 0, 0), new Vector3(1, 0, 0))); Assert.IsTrue(negativeXInfo == null); } { TriangleShape facingNegativeX = new TriangleShape(new Vector3(0, -1, -1), new Vector3(0, 0, 1), new Vector3(0, 1, -1), redStuff); IntersectInfo positiveXInfo = facingNegativeX.GetClosestIntersection(new Ray(new Vector3(1, 0, 0), new Vector3(-1, 0, 0))); Assert.IsTrue(positiveXInfo == null); IntersectInfo negativeXInfo = facingNegativeX.GetClosestIntersection(new Ray(new Vector3(-1, 0, 0), new Vector3(1, 0, 0))); Assert.IsTrue(negativeXInfo.HitPosition == new Vector3(0, 0, 0)); Assert.IsTrue(negativeXInfo.HitType == IntersectionType.FrontFace); Assert.IsTrue(negativeXInfo.ClosestHitObject == facingNegativeX); Assert.IsTrue(negativeXInfo.DistanceToHit == 1); } }
public override (double u, double v) GetUv(IntersectInfo info) { Vector3 vn = new Vector3(0, 1, 0).GetNormal(); // north pole / up Vector3 ve = new Vector3(0, 0, 1).GetNormal(); // equator / sphere orientation Vector3 vp = (info.HitPosition - position).GetNormal(); //points from center of sphere to intersection double phi = Math.Acos(-Vector3Ex.Dot(vp, vn)); double v = (phi * 2 / Math.PI) - 1; double sinphi = Vector3Ex.Dot(ve, vp) / Math.Sin(phi); sinphi = sinphi < -1 ? -1 : sinphi > 1 ? 1 : sinphi; double theta = Math.Acos(sinphi) * 2 / Math.PI; double u; if (Vector3Ex.Dot(Vector3Ex.Cross(vn, ve), vp) > 0) { u = theta; } else { u = 1 - theta; } return(u, v); }
public override IEnumerable IntersectionIterator(Ray ray) { double minDistFound; double maxDistFound; int minAxis; int maxAxis; if (intersect(ray, out minDistFound, out maxDistFound, out minAxis, out maxAxis)) { if ((ray.intersectionType & IntersectionType.FrontFace) == IntersectionType.FrontFace) { IntersectInfo info = new IntersectInfo(); info.hitType = IntersectionType.FrontFace; info.closestHitObject = this; info.hitPosition = ray.origin + ray.directionNormal * minDistFound; info.normalAtHit[minAxis] = ray.sign[minAxis] == Ray.Sign.negative ? 1 : -1; // you hit the side that is oposite your sign info.distanceToHit = minDistFound; yield return(info); } if ((ray.intersectionType & IntersectionType.BackFace) == IntersectionType.BackFace) { IntersectInfo info = new IntersectInfo(); info.hitType = IntersectionType.BackFace; info.closestHitObject = this; info.hitPosition = ray.origin + ray.directionNormal * maxDistFound; info.normalAtHit[maxAxis] = ray.sign[maxAxis] == Ray.Sign.negative ? 1 : -1; info.distanceToHit = maxDistFound; yield return(info); } } }
public static void ReadInList(List <IntersectInfo> listToPopulate, StreamReader reader) { int count; int.TryParse(reader.ReadLine(), NumberStyles.Number, null, out count); for (int i = 0; i < count; i++) { IntersectInfo info = new IntersectInfo(); string line = reader.ReadLine(); string[] strings = line.Split(','); if (strings[0] == IntersectionType.FrontFace.ToString()) { info.HitType = IntersectionType.FrontFace; } else { if (strings[0] != IntersectionType.BackFace.ToString()) { throw new Exception("Has to be back or front."); } info.HitType = IntersectionType.BackFace; } double.TryParse(strings[1], NumberStyles.Number, null, out info.DistanceToHit); listToPopulate.Add(info); } }
public IntersectInfo GetClosestIntersection(Ray ray) { if (ray.Intersection(Aabb)) { IPrimitive checkFirst = nodeA; IPrimitive checkSecond = nodeB; if (ray.directionNormal[splittingPlane] < 0) { checkFirst = nodeB; checkSecond = nodeA; } IntersectInfo infoFirst = checkFirst.GetClosestIntersection(ray); if (infoFirst != null && infoFirst.hitType != IntersectionType.None) { if (ray.isShadowRay) { return(infoFirst); } else { ray.maxDistanceToConsider = infoFirst.distanceToHit; } } if (checkSecond != null) { IntersectInfo infoSecond = checkSecond.GetClosestIntersection(ray); if (infoSecond != null && infoSecond.hitType != IntersectionType.None) { if (ray.isShadowRay) { return(infoSecond); } else { ray.maxDistanceToConsider = infoSecond.distanceToHit; } } if (infoFirst != null && infoFirst.hitType != IntersectionType.None && infoFirst.distanceToHit >= 0) { if (infoSecond != null && infoSecond.hitType != IntersectionType.None && infoSecond.distanceToHit < infoFirst.distanceToHit && infoSecond.distanceToHit >= 0) { return(infoSecond); } else { return(infoFirst); } } return(infoSecond); // we don't have to test it because it didn't hit. } return(infoFirst); } return(null); }
public IntersectInfo(IntersectInfo copyInfo) { this.hitType = copyInfo.hitType; this.closestHitObject = copyInfo.closestHitObject; this.hitPosition = copyInfo.hitPosition; this.normalAtHit = copyInfo.normalAtHit; this.distanceToHit = copyInfo.distanceToHit; }
public IntersectInfo(IntersectInfo copyInfo) { this.HitType = copyInfo.HitType; this.ClosestHitObject = copyInfo.ClosestHitObject; this.HitPosition = copyInfo.HitPosition; this.NormalAtHit = copyInfo.NormalAtHit; this.DistanceToHit = copyInfo.DistanceToHit; }
public ColorF FullyTraceRay(Ray ray, Scene scene, out IntersectInfo primaryInfo) { primaryInfo = TracePrimaryRay(ray, scene); if (primaryInfo.HitType != IntersectionType.None) { ColorF totalColor = CreateAndTraceSecondaryRays(primaryInfo, ray, scene, 0); return(totalColor); } return(scene.background.Color); }
public override (double u, double v) GetUv(IntersectInfo info) { Vector3Float normal = Plane.Normal; Vector3Float vecU = new Vector3Float(normal.Y, normal.Z, -normal.X); Vector3Float vecV = vecU.Cross(Plane.Normal); var u = new Vector3Float(info.HitPosition).Dot(vecU); var v = new Vector3Float(info.HitPosition).Dot(vecV); return(u, v); }
public override (double u, double v) GetUv(IntersectInfo info) { Vector3 Position = plane.Normal; Vector3 vecU = new Vector3(Position.Y, Position.Z, -Position.X); Vector3 vecV = Vector3Ex.Cross(vecU, plane.Normal); double u = Vector3Ex.Dot(info.HitPosition, vecU); double v = Vector3Ex.Dot(info.HitPosition, vecV); return(u, v); }
public override (double u, double v) GetUv(IntersectInfo info) { Vector3Float normal = Plane.Normal; Vector3Float vecU = new Vector3Float(normal.y, normal.z, -normal.x); Vector3Float vecV = Vector3Float.Cross(vecU, Plane.Normal); var u = Vector3Float.Dot(new Vector3Float(info.HitPosition), vecU); var v = Vector3Float.Dot(new Vector3Float(info.HitPosition), vecV); return(u, v); }
public ColorF GetColor(IntersectInfo info) { if (Material.HasTexture) { var uv = GetUv(info); return(Material.GetColor(uv.u, uv.v)); } else { return(Material.GetColor(0, 0)); } }
public override RGBA_Floats GetColor(IntersectInfo info) { if (Material.HasTexture) { throw new NotImplementedException(); } else { // skip uv calculation, just get the color return(this.Material.GetColor(0, 0)); } }
/// <summary> /// This implementation of intersect uses the fastest ray-sphere intersection algorithm I could find /// on the internet. /// </summary> /// <param name="ray"></param> /// <returns></returns> public override IntersectInfo GetClosestIntersection(Ray ray) { double radiusSquared = radius * radius; Vector3 deltaFromShpereCenterToRayOrigin = ray.origin - this.position; double distanceFromSphereCenterToRayOrigin = Vector3Ex.Dot(deltaFromShpereCenterToRayOrigin, ray.directionNormal); // negative means the sphere is in front of the ray. double lengthFromRayOrginToSphereCenterSquared = Vector3Ex.Dot(deltaFromShpereCenterToRayOrigin, deltaFromShpereCenterToRayOrigin); double lengthFromRayOrigintoNearEdgeOfSphereSquared = lengthFromRayOrginToSphereCenterSquared - radiusSquared; double distanceFromSphereCenterToRaySquared = distanceFromSphereCenterToRayOrigin * distanceFromSphereCenterToRayOrigin; double amountSphereCenterToRayIsGreaterThanRayOriginToEdgeSquared = distanceFromSphereCenterToRaySquared - lengthFromRayOrigintoNearEdgeOfSphereSquared; if (amountSphereCenterToRayIsGreaterThanRayOriginToEdgeSquared > 0 || (ray.intersectionType == IntersectionType.BackFace && lengthFromRayOrginToSphereCenterSquared < radiusSquared)) // yes, that's it, we found the intersection! { IntersectInfo info = new IntersectInfo(); info.closestHitObject = this; info.hitType = IntersectionType.FrontFace; if (ray.isShadowRay) { return(info); } double distanceFromRayOriginToSphereCenter = -distanceFromSphereCenterToRayOrigin; double amountSphereCenterToRayIsGreaterThanRayOriginToEdge = Math.Sqrt(amountSphereCenterToRayIsGreaterThanRayOriginToEdgeSquared); if (ray.intersectionType == IntersectionType.FrontFace) { double distanceToFrontHit = distanceFromRayOriginToSphereCenter - amountSphereCenterToRayIsGreaterThanRayOriginToEdge; if (distanceToFrontHit > ray.maxDistanceToConsider || distanceToFrontHit < ray.minDistanceToConsider) { return(null); } info.distanceToHit = distanceToFrontHit; info.HitPosition = ray.origin + ray.directionNormal * info.distanceToHit; info.normalAtHit = (info.HitPosition - position).GetNormal(); } else // check back faces { double distanceToBackHit = distanceFromRayOriginToSphereCenter + amountSphereCenterToRayIsGreaterThanRayOriginToEdge; if (distanceToBackHit > ray.maxDistanceToConsider || distanceToBackHit < ray.minDistanceToConsider) { return(null); } info.hitType = IntersectionType.BackFace; info.distanceToHit = distanceToBackHit; info.HitPosition = ray.origin + ray.directionNormal * info.distanceToHit; info.normalAtHit = -(info.HitPosition - position).GetNormal(); } return(info); } return(null); }
private static void TestSingleAngle(Scene scene, RayTracer raytracer, double advance, int i) { var sampleXY = new Vector2(48, 0); sampleXY.Rotate(advance * i); Vector3 rayOrigin = new Vector3(sampleXY, 10); Ray ray = new Ray(rayOrigin, -Vector3.UnitZ); IntersectInfo primaryInfo = raytracer.TracePrimaryRay(ray, scene); Assert.IsTrue(primaryInfo.HitType == IntersectionType.FrontFace, "always have a hit"); }
public IntersectInfo TracePrimaryRay(Ray ray, Scene scene) { IntersectInfo primaryRayIntersection = new IntersectInfo(); foreach (IPrimitive shapeToTest in scene.shapes) { IntersectInfo info = shapeToTest.GetClosestIntersection(ray); if (info != null && info.hitType != IntersectionType.None && info.distanceToHit < primaryRayIntersection.distanceToHit && info.distanceToHit >= 0) { primaryRayIntersection = info; } } return(primaryRayIntersection); }
public override RGBA_Floats GetColor(IntersectInfo info) { if (Material.HasTexture) { Vector3 Position = plane.planeNormal; Vector3 vecU = new Vector3(Position.y, Position.z, -Position.x); Vector3 vecV = Vector3.Cross(vecU, plane.planeNormal); double u = Vector3.Dot(info.hitPosition, vecU); double v = Vector3.Dot(info.hitPosition, vecV); return(Material.GetColor(u, v)); } else { return(Material.GetColor(0, 0)); } }
private static bool GetRemoveRegion(List <IntersectInfo> subtractList, int startIndex, out int regionStartIndex, out int regionEndIndex) { regionStartIndex = startIndex; regionEndIndex = startIndex; double insideCount = 0; int subtractCount = subtractList.Count; for (int subtractIndex = startIndex; subtractIndex < subtractCount; subtractIndex++) { IntersectInfo subtractInfo = subtractList[subtractIndex]; if (subtractInfo.HitType == IntersectionType.FrontFace) { if (insideCount == 0) { regionStartIndex = subtractIndex; } insideCount++; } else if (subtractInfo.HitType == IntersectionType.BackFace) { if (insideCount == 0) { throw new Exception("You should not have a back face without a matching front face."); } insideCount--; if (insideCount == 0) { // let's check that there is not another entry aligned exactly with this exit int nextIndex = subtractIndex + 1; if (nextIndex >= subtractCount || subtractList[subtractIndex].DistanceToHit + Ray.sameSurfaceOffset < subtractList[nextIndex].DistanceToHit) { // we have our subtract region regionEndIndex = subtractIndex; return(true); } } } else { throw new Exception("There should be no 'none's in the hit types."); } } return(false); }
public override IEnumerable IntersectionIterator(Ray ray) { double radiusSquared = radius * radius; Vector3 deltaFromShpereCenterToRayOrigin = ray.origin - this.position; double distanceFromSphereCenterToRayOrigin = Vector3Ex.Dot(deltaFromShpereCenterToRayOrigin, ray.directionNormal); // negative means the sphere is in front of the ray. double lengthFromRayOrginToSphereCenterSquared = Vector3Ex.Dot(deltaFromShpereCenterToRayOrigin, deltaFromShpereCenterToRayOrigin); double lengthFromRayOrigintoNearEdgeOfSphereSquared = lengthFromRayOrginToSphereCenterSquared - radiusSquared; double distanceFromSphereCenterToRaySquared = distanceFromSphereCenterToRayOrigin * distanceFromSphereCenterToRayOrigin; double amountSphereCenterToRayIsGreaterThanRayOriginToEdgeSquared = distanceFromSphereCenterToRaySquared - lengthFromRayOrigintoNearEdgeOfSphereSquared; if (amountSphereCenterToRayIsGreaterThanRayOriginToEdgeSquared > 0) { double distanceFromRayOriginToSphereCenter = -distanceFromSphereCenterToRayOrigin; double amountSphereCenterToRayIsGreaterThanRayOriginToEdge = Math.Sqrt(amountSphereCenterToRayIsGreaterThanRayOriginToEdgeSquared); if ((ray.intersectionType & IntersectionType.FrontFace) == IntersectionType.FrontFace) { IntersectInfo info = new IntersectInfo(); info.hitType = IntersectionType.FrontFace; info.closestHitObject = this; double distanceToFrontHit = distanceFromRayOriginToSphereCenter - amountSphereCenterToRayIsGreaterThanRayOriginToEdge; info.distanceToHit = distanceToFrontHit; info.HitPosition = ray.origin + ray.directionNormal * info.distanceToHit; info.normalAtHit = (info.HitPosition - position).GetNormal(); yield return(info); } if ((ray.intersectionType & IntersectionType.BackFace) == IntersectionType.BackFace) { IntersectInfo info = new IntersectInfo(); info.hitType = IntersectionType.BackFace; info.closestHitObject = this; double distanceToBackHit = distanceFromRayOriginToSphereCenter + amountSphereCenterToRayIsGreaterThanRayOriginToEdge; info.distanceToHit = distanceToBackHit; info.HitPosition = ray.origin + ray.directionNormal * info.distanceToHit; info.normalAtHit = -(info.HitPosition - position).GetNormal(); yield return(info); } } }
public override IntersectInfo GetClosestIntersection(Ray ray) { IntersectInfo info = new IntersectInfo(); double minDistFound; double maxDistFound; int minAxis; int maxAxis; if (intersect(ray, out minDistFound, out maxDistFound, out minAxis, out maxAxis)) { if (ray.intersectionType == IntersectionType.FrontFace) { if (minDistFound > ray.minDistanceToConsider && minDistFound < ray.maxDistanceToConsider) { info.hitType = IntersectionType.FrontFace; if (ray.isShadowRay) { return(info); } info.closestHitObject = this; info.hitPosition = ray.origin + ray.directionNormal * minDistFound; info.normalAtHit[minAxis] = ray.sign[minAxis] == Ray.Sign.negative ? 1 : -1; // you hit the side that is oposite your sign info.distanceToHit = minDistFound; } } else // check back faces { if (maxDistFound > ray.minDistanceToConsider && maxDistFound < ray.maxDistanceToConsider) { info.hitType = IntersectionType.BackFace; if (ray.isShadowRay) { return(info); } info.closestHitObject = this; info.hitPosition = ray.origin + ray.directionNormal * maxDistFound; info.normalAtHit[maxAxis] = ray.sign[maxAxis] == Ray.Sign.negative ? 1 : -1; info.distanceToHit = maxDistFound; } } } return(info); }
public IntersectInfo GetClosestIntersection(Ray ray) { IntersectInfo bestInfo = null; foreach (IPrimitive item in Items) { IntersectInfo info = item.GetClosestIntersection(ray); if (info != null && info.hitType != IntersectionType.None && info.distanceToHit >= 0) { if (bestInfo == null || info.distanceToHit < bestInfo.distanceToHit) { bestInfo = info; } } } return(bestInfo); }
public override IntersectInfo GetClosestIntersection(Ray ray) { bool inFront; double distanceToHit = plane.GetDistanceToIntersection(ray, out inFront); if (distanceToHit > 0) { IntersectInfo info = new IntersectInfo(); info.closestHitObject = this; info.hitType = IntersectionType.FrontFace; info.hitPosition = ray.origin + ray.directionNormal * distanceToHit; info.normalAtHit = plane.planeNormal; info.distanceToHit = distanceToHit; return(info); } return(null); }
public override void OnMouseDown(MouseEventArgs mouseEvent) { base.OnMouseDown(mouseEvent); lastMouseMovePoint.x = mouseEvent.X; lastMouseMovePoint.y = mouseEvent.Y; if (Focused && MouseCaptured) { if (trackBallController.CurrentTrackingType == TrackBallController.MouseDownType.None) { if (Focused && MouseCaptured && mouseEvent.Button == MouseButtons.Left) { trackBallController.OnMouseDown(lastMouseMovePoint, Matrix4X4.Identity); } else if (mouseEvent.Button == MouseButtons.Middle) { trackBallController.OnMouseDown(lastMouseMovePoint, Matrix4X4.Identity, TrackBallController.MouseDownType.Translation); } } if (MouseCaptured) { lastMouseMovePoint.x = mouseEvent.X; lastMouseMovePoint.y = mouseEvent.Y; cameraDataAtStartOfMouseTracking = cameraData; cameraDataAtStartOfMouseTracking.cameraMatrix = scene.camera.axisToWorld; Ray rayAtPoint = scene.camera.GetRay(lastMouseMovePoint.x, lastMouseMovePoint.y); IntersectInfo info = raytracer.TracePrimaryRay(rayAtPoint, scene); if (info != null) { focusedObject = (BaseShape)info.closestHitObject; if (focusedObject != null && mouseEvent.Clicks == 2) { cameraData.lookAtPoint = focusedObject.GetAxisAlignedBoundingBox().Center; OrientCamera(); } } } } }
public override IntersectInfo GetClosestIntersection(Ray ray) { bool inFront; float distanceToHit; if (plane.RayHitPlane(ray, out distanceToHit, out inFront)) { bool wantFrontAndInFront = (ray.intersectionType & IntersectionType.FrontFace) == IntersectionType.FrontFace && inFront; bool wantBackAndInBack = (ray.intersectionType & IntersectionType.BackFace) == IntersectionType.BackFace && !inFront; if (wantFrontAndInFront || wantBackAndInBack) { Vector3 hitPosition = ray.origin + ray.directionNormal * distanceToHit; bool haveHitIn2D = false; if (majorAxis == 0) { haveHitIn2D = Check2DHitOnMajorAxis(hitPosition.y, hitPosition.z); } else if (majorAxis == 1) { haveHitIn2D = Check2DHitOnMajorAxis(hitPosition.x, hitPosition.z); } else { haveHitIn2D = Check2DHitOnMajorAxis(hitPosition.x, hitPosition.y); } if (haveHitIn2D) { IntersectInfo info = new IntersectInfo(); info.closestHitObject = this; info.hitType = IntersectionType.FrontFace; info.hitPosition = hitPosition; info.normalAtHit = new Vector3(plane.planeNormal); info.distanceToHit = distanceToHit; return(info); } } } return(null); }
public override RGBA_Floats GetColor(IntersectInfo info) { if (Material.HasTexture) { throw new NotImplementedException(); #if false //Vector vecU = new Vector(hit.y - Position.y, hit.z - Position.z, Position.x-hit.x); Vector3 Position = Transform.Position; Vector3 vecU = new Vector3D((P1.y + P2.y) / 2 - Position.y, (P1.z + P2.z) / 2 - Position.z, Position.x - (P1.x + P2.x) / 2).GetNormal(); Vector3 vecV = vecU.Cross((P1 + P2) / 2 - Position).GetNormal(); double u = Vector3.Dot(info.hitPosition, vecU); double v = Vector3.Dot(info.hitPosition, vecV); return(Material.GetColor(u, v)); #endif } else { return(Material.GetColor(0, 0)); } }
public void FullyTraceRayBundle(RayBundle rayBundle, IntersectInfo[] intersectionsForBundle, Scene scene) { TracePrimaryRayBundle(rayBundle, intersectionsForBundle, scene); for (int i = 0; i < rayBundle.rayArray.Length; i++) { try { IntersectInfo primaryInfo = TracePrimaryRay(rayBundle.rayArray[i], scene); if (intersectionsForBundle[i].HitType != IntersectionType.None) { intersectionsForBundle[i].TotalColor = CreateAndTraceSecondaryRays(primaryInfo, rayBundle.rayArray[i], scene, 0); } else { intersectionsForBundle[i].TotalColor = scene.background.Color; } } catch { } } }
private static bool IsInside(List <IntersectInfo> listToCheck, double distance) { int insideCount = 0; int conut = listToCheck.Count; for (int index = 0; index < conut; index++) { IntersectInfo info = listToCheck[index]; if (info.hitType == HitType.FrontFace) { if (insideCount == 0) { startSubtract = info.distanceToHit; // add all the elements from the last end to the new start } insideCount++; } else if (info.hitType == HitType.BackFace) { insideCount--; if (insideCount == 0) { if (IsInside(allPrimary, info.distanceToHit)) { result.Add(info); } lastSubtractEnd = info.distanceToHit; // add all the front face points between lastSubtractEnd and startSubtract } } else { throw new Exception("There should be no 'none's in the hit types."); } } return(false); }
public void DiscoveredBadIntersectInfoListSubtraction() { string primaryString = @"2 FrontFace, 6.55505298172777 BackFace, 7.05554361306285"; string subtractString = @"4 FrontFace, 7.05554387355765 BackFace, 7.14478176419901 FrontFace, 7.28926063619785 BackFace, 7.36209329430552"; List <IntersectInfo> allPrimary = new List <IntersectInfo>(); IntersectInfo.ReadInList(allPrimary, new StreamReader(new MemoryStream(Encoding.ASCII.GetBytes(primaryString)))); List <IntersectInfo> allSubtract = new List <IntersectInfo>(); IntersectInfo.ReadInList(allSubtract, new StreamReader(new MemoryStream(Encoding.ASCII.GetBytes(subtractString)))); List <IntersectInfo> result = new List <IntersectInfo>(); IntersectInfo.Subtract(allPrimary, allSubtract, result); }
public override RGBA_Floats GetColor(IntersectInfo info) { if (Material.HasTexture) { Vector3 vn = new Vector3(0, 1, 0).GetNormal(); // north pole / up Vector3 ve = new Vector3(0, 0, 1).GetNormal(); // equator / sphere orientation Vector3 vp = (info.hitPosition - position).GetNormal(); //points from center of sphere to intersection double phi = Math.Acos(-Vector3.Dot(vp, vn)); double v = (phi * 2 / Math.PI) - 1; double sinphi = Vector3.Dot(ve, vp) / Math.Sin(phi); sinphi = sinphi < -1 ? -1 : sinphi > 1 ? 1 : sinphi; double theta = Math.Acos(sinphi) * 2 / Math.PI; double u; if (Vector3.Dot(Vector3.Cross(vn, ve), vp) > 0) { u = theta; } else { u = 1 - theta; } // alternative but worse implementation //double u = Math.Atan2(vp.x, vp.z); //double v = Math.Acos(vp.y); return(this.Material.GetColor(u, v)); } else { // skip uv calculation, just get the color return(this.Material.GetColor(0, 0)); } }
public override void OnMouseMove(MouseEventArgs mouseEvent) { base.OnMouseMove(mouseEvent); if (trackBallController.CurrentTrackingType != TrackBallController.MouseDownType.None) { lastMouseMovePoint.x = mouseEvent.X; lastMouseMovePoint.y = mouseEvent.Y; trackBallController.OnMouseMove(lastMouseMovePoint); NeedRedraw = true; Invalidate(); } if (Focused && MouseCaptured) { lastMouseMovePoint.x = mouseEvent.X; lastMouseMovePoint.y = mouseEvent.Y; cameraData = cameraDataAtStartOfMouseTracking; //cameraData.Rotate(trackBallRotation); //OrientCamera(); } lastMouseMovePoint.x = mouseEvent.X; lastMouseMovePoint.y = mouseEvent.Y; Ray rayAtPoint = scene.camera.GetRay(lastMouseMovePoint.x, lastMouseMovePoint.y); IntersectInfo primaryInfo = raytracer.TracePrimaryRay(rayAtPoint, scene); if (primaryInfo.hitType != IntersectionType.None) { mouseOverColor = raytracer.CreateAndTraceSecondaryRays(primaryInfo, rayAtPoint, scene, 0); } }