public IntersectionResult( Mesh mesh, Triangle triangle, float u, float v, float d, Vector3 worldPosition) { this.mesh = mesh; this.triangle = triangle; this.u = u; this.v = v; this.d = d; this.worldPosition = worldPosition; }
private void FindVertices(NodeContent node) { MeshContent mesh = node as MeshContent; if (mesh != null) { List<Triangle> triangles = new List<Triangle>(); Matrix absoluteTransform = mesh.AbsoluteTransform; Matrix absoluteTransformInvertTranspose = Matrix.Transpose(Matrix.Invert(absoluteTransform)); foreach (GeometryContent geometry in mesh.Geometry) { VertexChannel texCoords = null; if (geometry.Vertices.Channels.Contains(VertexChannelNames.TextureCoordinate(0))) { texCoords = geometry.Vertices.Channels[VertexChannelNames.TextureCoordinate(0)]; } else if (this.UseTexture) { throw new InvalidContentException("Model is built with UseTexure but does not contain any TextureCoordinates."); } VertexChannel normals = null; if(geometry.Vertices.Channels.Contains(VertexChannelNames.Normal(0))) { normals = geometry.Vertices.Channels[VertexChannelNames.Normal(0)]; } VertexChannel colors = null; if (geometry.Vertices.Channels.Contains(VertexChannelNames.Color(0))) { colors = geometry.Vertices.Channels[VertexChannelNames.Color(0)]; } BasicMaterialContent basicMaterial = geometry.Material as BasicMaterialContent; int triangleIndex = 0; for (int i = 0; i < geometry.Indices.Count; i += 3) { Vector3 v1 = geometry.Vertices.Positions[geometry.Indices[i]]; Vector3 v2 = geometry.Vertices.Positions[geometry.Indices[i + 1]]; Vector3 v3 = geometry.Vertices.Positions[geometry.Indices[i + 2]]; Vector3 n1 = (Vector3)normals[geometry.Indices[i]]; Vector3 n2 = (Vector3)normals[geometry.Indices[i + 1]]; Vector3 n3 = (Vector3)normals[geometry.Indices[i + 2]]; Vector3.Transform(ref v1, ref absoluteTransform, out v1); Vector3.Transform(ref v2, ref absoluteTransform, out v2); Vector3.Transform(ref v3, ref absoluteTransform, out v3); // I have no idea why I have to do this. If I do not, the Z and Y components of all normals are "switched" around. //Vector3.Transform(ref n1, ref normalTransform, out n1); //Vector3.Transform(ref n2, ref normalTransform, out n2); //Vector3.Transform(ref n3, ref normalTransform, out n3); // I am suspecting that what I suspected above is wrong. I mustve confused myself with weird models. // It seems like the normals should be transformed by the absoluteTransfom; //--- // SCRATCH THAT. The normals should be transformed by the transpose of the inverse of the absolute transform :D Vector3.Transform(ref n1, ref absoluteTransformInvertTranspose, out n1); Vector3.Transform(ref n2, ref absoluteTransformInvertTranspose, out n2); Vector3.Transform(ref n3, ref absoluteTransformInvertTranspose, out n3); n1.Normalize(); n2.Normalize(); n3.Normalize(); Vector3 edge1, edge2, surfaceNormal; Vector3.Subtract(ref v2, ref v1, out edge1); Vector3.Subtract(ref v3, ref v1, out edge2); Vector3.Cross(ref edge2, ref edge1, out surfaceNormal); surfaceNormal.Normalize(); Triangle triangle = new Triangle(); triangle.v1 = v1; triangle.v2 = v2; triangle.v3 = v3; if (texCoords != null) { triangle.uv1 = (Vector2)texCoords[geometry.Indices[i]]; triangle.uv2 = (Vector2)texCoords[geometry.Indices[i + 1]]; triangle.uv3 = (Vector2)texCoords[geometry.Indices[i + 2]]; } triangle.n1 = n1; triangle.n2 = n2; triangle.n3 = n3; triangle.i1 = geometry.Indices[i]; triangle.i2 = geometry.Indices[i + 1]; triangle.i3 = geometry.Indices[i + 2]; triangle.id = triangleIndex++; triangle.surfaceNormal = surfaceNormal; if (this.UseVertexColors && colors != null) triangle.color = ((Color)colors[geometry.Indices[i]]).ToVector4(); else triangle.color = this.DiffuseColor.ToVector4(); triangles.Add(triangle); } } BoundingBox box = this.CreateBoundingBox(triangles); Mesh tracerMesh = new Mesh(triangles.ToArray(), this.material, box); this.meshes.Add(tracerMesh); } foreach (NodeContent child in node.Children) { this.FindVertices(child); } }
public void CastRay(ref Ray ray, out Color resultColor, int iteration, Triangle origin, Mesh ignoreObject, float currentRefIndex) { resultColor = Color.White; RayTraceProject.Spatial.IntersectionResult? nullableResult; if (CurrentScene.GetRayIntersection(ref ray, out nullableResult, origin, ignoreObject)) { RayTraceProject.Spatial.IntersectionResult result = (RayTraceProject.Spatial.IntersectionResult)nullableResult; Material material = result.mesh.MeshMaterial; Vector3 fragmentNormal; // Calculate intersection normal if (material.InterpolateNormals) { Vector3 n1 = result.triangle.n2 - result.triangle.n1; Vector3 n2 = result.triangle.n3 - result.triangle.n1; fragmentNormal = result.triangle.n1 + (n1 * result.u) + (n2 * result.v); fragmentNormal.Normalize(); } else { fragmentNormal = result.triangle.surfaceNormal; } // Calculate light Vector3 lightResult = Vector3.Zero; for (int i = 0; i < this.lights.Count; i++) { float lightAmount = this.IsLightPathObstructed(result, this.lights[i]); if (lightAmount != 1f) { lightResult += this.lights[i].GetLightForFragment(result.worldPosition, fragmentNormal) * (1.0f - lightAmount); } } addRayPoints(ray.Position, result.worldPosition, Color.White); if (iteration < this.MaxReflections) { Ray r = new Ray(); r.Position = result.worldPosition; r.Direction = Vector3.Reflect(ray.Direction, fragmentNormal); r.Direction.Normalize(); Color reflectionColor; // If the triangle is part of convex geometry, there is no need to check for collisions for this next ray. if (result.triangle.convexGeometry) this.CastRay(ref r, out reflectionColor, iteration + 1, result.triangle, result.mesh, currentRefIndex); else this.CastRay(ref r, out reflectionColor, iteration + 1, result.triangle, null, currentRefIndex); Vector3 surfaceColor; #if DEBUG_NORMALS resultColor = new Color(fragmentNormal); #elif DEBUG_CONVEXFLAG resultColor = result.triangle.convexGeometry ? Color.Green : Color.Red; #else if (material.UseTexture) { Vector2 uv1 = (result.triangle.uv2) - (result.triangle.uv1); Vector2 uv2 = (result.triangle.uv3) - (result.triangle.uv1); Vector2 interpolatedUV = (result.triangle.uv1) + (uv1 * result.u) + (uv2 * result.v); material.LookupUV(interpolatedUV, this.AddressMode, this.TextureFiltering, out surfaceColor); } else { surfaceColor.X = result.triangle.color.X; surfaceColor.Y = result.triangle.color.Y; surfaceColor.Z = result.triangle.color.Z; } //addRayPoints(ray.Position, result.worldPosition, Color.White ); Vector3 colorVector = Vector3.Lerp(reflectionColor.ToVector3(), surfaceColor, 1.0f - material.Reflectiveness) * lightResult; if (material.Transparent) { ////addRayPoints(ray.Position, result.worldPosition, Color.White); //float theta1; //float n1, n2; //if (currentRefIndex == material.RefractionIndex) //{ // // Ray is inside material. Exiting to vacuum. // n1 = currentRefIndex; // n2 = 1.0f; // Index of vacuum. // //theta1 = Math.Abs(Vector3.Dot(fragmentNormal, ray.Direction)); // theta1 = 1 - Vector3.Dot(fragmentNormal, ray.Direction); // //addRayPoints(ray.Position, ray.Position + (ray.Direction * 10), Color.Red); // //theta1 = -theta1; //} //else //{ // n1 = currentRefIndex; // n2 = material.RefractionIndex; // theta1 = 1 - Vector3.Dot(fragmentNormal, -ray.Direction); //} // //} // ////// SER DET INTE UT SOM EN SKÅLFORMAD LINS? DEN SKA INVERTERAS! // // n1 * sin(vinkel1) = n2 * sin(vinkel2) // // (n1 * sin(vinkel1) / n2 = sin(vinkel2) // // Jag tror inte jag behöver ta sinus av theta1, det är redan en sine. //float theta2 = n1 * (float)Math.Asin((theta1) / n2); // //float theta2 = (float)Math.Asin((n1 * theta1) / n2); // Vector3 refraction; // if (float.IsNaN(theta1) || float.IsNaN(theta2)) // { // colorVector = Vector3.UnitZ; // refraction = ray.Direction; // } // else if (theta1 != 0.0f && theta2 != 0.0f) // { // //addRayPoints(ray.Position, result.worldPosition, new Color(1, 0, 0)); // Vector3 rotateAxis = Vector3.Cross(fragmentNormal, ray.Direction); // //addRayPoints(result.worldPosition, result.worldPosition + rotateAxis, Color.Red); // //addRayPoints(result.worldPosition, result.worldPosition + fragmentNormal, Color.Green); // Matrix rotateMatrix = Matrix.CreateFromAxisAngle(rotateAxis, theta2); // refraction = Vector3.Transform(-fragmentNormal, rotateMatrix); // } // else // { // refraction = ray.Direction; // } // ray.Position = result.worldPosition; // ray.Direction = refraction; // ray.Direction.Normalize(); ////addRayPoints(result.worldPosition, (refraction * 1000), new Color(red, red, red)); ////red = (red + (255 / 2)) % 255; //Color refractColor; //this.CastRay(ref ray, out refractColor, iteration + 1, result.triangle, null, n2); ////addRayPoints(ray.Position, ray.Direction * 100, new Color(0, theta1, theta2)); //colorVector = Vector3.Lerp(refractColor.ToVector3(), colorVector, result.triangle.color.W); // --- Avkommentera allt ovan. float n1, n2; if (currentRefIndex == material.RefractionIndex) { n1 = 1.0f; n2 = currentRefIndex; } else { n1 = material.RefractionIndex; n2 = 1.0f; } //n1 = 0.9f; //n2 = 1.0f; //ray.Direction = new Vector3(0.707107f, -0.707107f, 0); //fragmentNormal = new Vector3(0, 1, 0); float cos1 = Vector3.Dot(fragmentNormal, -ray.Direction); float cos2 = (float)Math.Sqrt(1 - Math.Pow(n1 / n2, 2.0) * (1 - Math.Pow(cos1, 2.0))); float theta1 = (float)Math.Acos(cos1); float theta2 = (float)Math.Acos(cos2); Vector3 refract; if (cos1 >= 0) { refract = (n1 / n2) * ray.Direction + ((n1 / n2) * cos1 - cos2) * fragmentNormal; } else { refract = (n1 / n2) * ray.Direction - ((n1 / n2) * cos1 - cos2) * fragmentNormal; } ray.Position = result.worldPosition; ray.Direction = refract; ray.Direction.Normalize(); Color refractColor; this.CastRay(ref ray, out refractColor, iteration + 1, result.triangle, null, n2); colorVector = Vector3.Lerp(refractColor.ToVector3(), colorVector, result.triangle.color.W); addRayPoints(ray.Position, ray.Direction * 100, Color.Red); } resultColor = new Color(colorVector); #endif } else { Vector3 surfaceColor; if (material.UseTexture) { Vector2 uv1 = (result.triangle.uv2) - (result.triangle.uv1); Vector2 uv2 = (result.triangle.uv3) - (result.triangle.uv1); Vector2 interpolatedUV = (result.triangle.uv1) + (uv1 * result.u) + (uv2 * result.v); material.LookupUV(interpolatedUV, this.AddressMode, this.TextureFiltering, out surfaceColor); } else { surfaceColor.X = result.triangle.color.X; surfaceColor.Y = result.triangle.color.Y; surfaceColor.Z = result.triangle.color.Z; } resultColor = new Color(lightResult * surfaceColor); } } else // Ray does not intersect any object. { resultColor = new Color(Vector3.Zero); //addRayPoints(ray.Position, ray.Position + (1000 * ray.Direction)); } }
public bool GetRayIntersection(ref Ray ray, out IntersectionResult? result, Triangle ignoreTriangle, Mesh ignoreObject) { result = null; SortedList<float, List<CubeNode>> cubeoids = new SortedList<float, List<CubeNode>>(); //SortedDictionary<float, CubeNode> cubeoids = new SortedDictionary<float, CubeNode>(); this.GetRayCubeNodeIntersections(ref ray, this.root, cubeoids); if (cubeoids.Count == 0) return false; List<List<CubeNode>> intersectedCubeoids = cubeoids.Values.ToList(); int cubeoidIndex = 0; float minDistance = float.MaxValue; bool intersectionFound = false; Mesh intersectedMesh = null; SceneObject intersectedSceneObject = null; RayTracerTypeLibrary.MeshOctree.TriangleIntersectionResult? intersectedTriangleResult = null; RayTracerTypeLibrary.MeshOctree.TriangleIntersectionResult? triangleResult; Vector3 v1, v2, rayDirPosition; while (!intersectionFound && cubeoidIndex < cubeoids.Count) { List<CubeNode> cuboidGroup = intersectedCubeoids[cubeoidIndex++]; for (int k = 0; k < cuboidGroup.Count; k++) { List<ISpatialBody> objects = cuboidGroup[k].containingObjects; for (int i = 0; i < objects.Count; i++) { if (ignoreObject == null || ignoreObject != objects[i]) { SceneObject sceneObject = (SceneObject)objects[i]; Matrix inverseWorld = sceneObject.InverseWorld; // -- While the below LOOKS like it should work, it does not. Correct solution below! //Ray transformedRay; //Vector3.Transform(ref ray.Position, ref inverseWorld, out transformedRay.Position); //Vector3.Transform(ref ray.Direction, ref inverseWorld, out transformedRay.Direction); //transformedRay.Direction.Normalize(); //Vector3 v1 = Vector3.Transform(ray.Position, inverseWorld); //Vector3 v2 = Vector3.Transform(ray.Position + ray.Direction, inverseWorld); Vector3.Add(ref ray.Position, ref ray.Direction, out rayDirPosition); Vector3.Transform(ref ray.Position, ref inverseWorld, out v1); Vector3.Transform(ref rayDirPosition, ref inverseWorld, out v2); Vector3.Subtract(ref v2, ref v1, out rayDirPosition); Ray transformedRay = new Ray(v1, rayDirPosition); transformedRay.Direction.Normalize(); for (int meshIndex = 0; meshIndex < sceneObject.Meshes.Count; meshIndex++) { if (sceneObject.Meshes[meshIndex].RayIntersects(ref transformedRay)) { if (sceneObject.Meshes[meshIndex].Octree.GetRayIntersection(ref transformedRay, out triangleResult, ignoreTriangle) && triangleResult.Value.d < minDistance) { minDistance = triangleResult.Value.d; intersectedTriangleResult = triangleResult; intersectedMesh = sceneObject.Meshes[meshIndex]; intersectedSceneObject = sceneObject; intersectionFound = true; } //Triangle[] triangles = sceneObject.Meshes[meshIndex].Triangles; //// Backface culling IF first triangle (and thus the rest) is transparent. Rewrite this in the future. //if (sceneObject.Meshes[meshIndex].MeshMaterial.Transparent) //{ // for (int j = 0; j < triangles.Length; j++) // { // if (ignoreTriangle == null || ignoreTriangle != triangles[j]) // { // float currentU, currentV, distance; // if (transformedRay.IntersectsTriangle(triangles[j], out currentU, out currentV, out distance) && // distance < minDistance) // { // minDistance = distance; // intersectionU = currentU; // intersectionV = currentV; // intersectedTriangle = triangles[j]; // intersectedMesh = sceneObject.Meshes[meshIndex]; // intersectedSceneObject = sceneObject; // // Signal that intersection was found. Remaining objects in this cubeoid will be examined, but no more cubeoids. // intersectionFound = true; // } // } // } //} //else //{ // for (int j = 0; j < triangles.Length; j++) // { // if (ignoreTriangle == null || ignoreTriangle != triangles[j]) // { // float currentU, currentV, distance; // if (transformedRay.IntersectsTriangleBackfaceCulling(triangles[j], out currentU, out currentV, out distance) && // distance < minDistance) // { // minDistance = distance; // intersectionU = currentU; // intersectionV = currentV; // intersectedTriangle = triangles[j]; // intersectedMesh = sceneObject.Meshes[meshIndex]; // intersectedSceneObject = sceneObject; // // Signal that intersection was found. Remaining objects in this cubeoid will be examined, but no more cubeoids. // intersectionFound = true; // } // } // } //} } } } } } } if (intersectionFound) { Vector3 interpolatedPosition; Matrix world = intersectedSceneObject.World; Vector3 objectSpacePosition = intersectedTriangleResult.Value.objectSpacePosition; Vector3.Transform(ref objectSpacePosition, ref world, out interpolatedPosition); result = new IntersectionResult( intersectedMesh, intersectedTriangleResult.Value.triangle, intersectedTriangleResult.Value.u, intersectedTriangleResult.Value.v, minDistance, interpolatedPosition); } return intersectionFound; }