public void SimpleQuad_ShouldBeMissed() { var vertices = new Vector3[] { new Vector3(-1, 0, -1), new Vector3(1, 0, -1), new Vector3(1, 0, 1), new Vector3(-1, 0, 1) }; var indices = new int[] { 0, 1, 2, 0, 2, 3 }; TriangleMesh mesh = new TriangleMesh(vertices, indices); var rt = new Raytracer(); rt.AddMesh(mesh); rt.CommitScene(); Hit hit = rt.Trace(new Ray { Origin = new Vector3(-0.5f, -10, 0), Direction = new Vector3(0, -1, 0), MinDistance = 1.0f }); Assert.False(hit); }
public virtual RgbColor StartFromEmitter(EmitterSample emitterSample, RgbColor initialWeight) { isOnLightSubpath = true; Ray ray = Raytracer.SpawnRay(emitterSample.Point, emitterSample.Direction); return(ContinueWalk(ray, emitterSample.Point, emitterSample.Pdf, initialWeight, 1)); }
private void Awake() { Instance = this; _meshRenderer = GetComponent <MeshRenderer>(); _meshRenderer.sharedMaterial = Instantiate(_meshRenderer.sharedMaterial); _objects = sceneObject.GetComponentsInChildren <RTObject>().ToList(); }
public void SimpleQuad_ShouldBeIntersected() { var vertices = new Vector3[] { new Vector3(-1, 0, -1), new Vector3(1, 0, -1), new Vector3(1, 0, 1), new Vector3(-1, 0, 1) }; var indices = new int[] { 0, 1, 2, 0, 2, 3 }; TriangleMesh mesh = new(vertices, indices); using var rt = new Raytracer(); rt.AddMesh(mesh); rt.CommitScene(); Hit hit = rt.Trace(new Ray { Origin = new Vector3(-0.5f, -10, 0), Direction = new Vector3(0, 1, 0), MinDistance = 1.0f }); Assert.Equal(10.0f, hit.Distance, 0); Assert.Equal(1u, hit.PrimId); Assert.Equal(mesh, hit.Mesh); }
public override Vector3 GetIntensity(Ray ray, Intersection intersection, Scene scene) { float diff = 0f, spec = 0f; Material mat = intersection.primitive.material; bool visible = false; // Calculate vector from intersection point to light point Vector3 L = direction; float NdotL = Vector3.Dot(intersection.normal, L); // Is the light direction on the same side as the normal? if (NdotL > 0f) { // check the intersection as late as possible: visible = !scene.DoesIntersect(new Ray(intersection.location, L), float.PositiveInfinity); if (visible) { diff = NdotL; if (ray.debug) { // Add a shadow ray to the debug screen Ray debugray = new Ray(intersection.location, L); debugray.debugSurface = ray.debugSurface; debugray.camerapos = ray.camerapos; debugray.debugxunit = ray.debugxunit; debugray.debugyunit = ray.debugyunit; Raytracer.DrawRayDebug(debugray, null, 0x0000ff); } } } // Add some specularity if (mat.isSpecular) { Vector3 viewDir = -ray.direction; Vector3 halfway = (L + viewDir).Normalized(); float NdotH = Vector3.Dot(intersection.normal, halfway); // Are we on the good side of the normal? if (NdotH > 0f) { // Check if this light is visible from the intersection (calculate if not calculated already) if (NdotL <= 0f) { visible = !scene.DoesIntersect(new Ray(intersection.location, L), float.PositiveInfinity); } if (visible) { // Use a power law to let this hardness have on effect on the decay of intensity of the white effect spec = (float)Math.Pow(NdotH, mat.hardness); } } } // mix the colors, specularity (from light, not reflection) is always white. Vector3 diffuse = intersection.primitive.GetDiffuseColor(intersection); return(intensity * (diffuse * diff + Vector3.One * spec)); }
// initialize public void Init() //if camera is set at a point other than the origin, you first have to move the camera to get the right view. { rayTracer = new Raytracer(); a = new Application(); c = new Camera(new Vector3(0, 0, 0), new Vector3(0, 0, 1), 90); //set FOV here c.direction = c.ProcessTarget(c.position + new Vector3(0, 0, 1)); //set target here c.Screen(); s = new Scene(); }
private static byte[] Lab05() { Raytracer raytracer = new Raytracer(Premade.Lab05.Camera(), Premade.Lab05.Scene()) { ShadowSamples = 5, SuperSampling = 50 }; return(raytracer.CalculatePixelsByteArray(imageResolution, imageResolution)); }
private static byte[] DepthOfField() { Raytracer raytracer = new Raytracer(Premade.DepthOfField.Camera(), Premade.DepthOfField.Scene()) { SuperSampling = 512, ShadowSamples = 4 }; return(raytracer.CalculatePixelsByteArray(imageResolution, imageResolution)); }
public override Color GetColor(Raytracer raytracer, Ray ray, RaycastHit hit, TraceData traceData) { Color lightSumColor = RenderSettings.ambientLight; var lights = Light.GetLights(LightType.Point, 0); foreach (var light in lights) { Vector3 vToLight = light.transform.position - hit.point; float distance = vToLight.magnitude; if (distance >= light.range) { continue; } float lightVolumeRadius = distance * 0.2f; distance = Mathf.Abs(distance - lightVolumeRadius); Vector3 dir = vToLight.normalized; float diffuseIntensity = Vector3.Dot(dir, hit.normal); if (diffuseIntensity <= 0) { continue; } diffuseIntensity = Mathf.Pow(diffuseIntensity, DiffuseExponent); float attenuation; switch (Attenuation) { case AttenuationKind.Logarithmic: const float AttenuationBase = 2.7f; attenuation = 1.0f / Mathf.Log(distance + AttenuationBase, AttenuationBase); break; case AttenuationKind.Quadratic: attenuation = 1 - distance / light.range; attenuation = attenuation * attenuation; break; case AttenuationKind.Linear: default: attenuation = 1 - distance / light.range; break; } Color lightColor = light.color * attenuation * diffuseIntensity * light.intensity * LightIntensityFactor; lightSumColor += lightColor; } Color totalColor = lightSumColor * this.Color; return(totalColor); }
protected override void OnInitialize() { var scene = Scenes[1].GetScene(WIDTH, HEIGHT); Raytracer.LoadScene(scene); Raytracer.FrameBuffer = new RgbaFloat[WIDTH * HEIGHT]; CreateDeviceResources(); CommandLists.Add(commandList); Window.Resized += OnResized; base.OnInitialize(); OnResized(); }
private void Start() { Screen.SetResolution(renderWindow.ResolutionWidth, renderWindow.ResolutionHeight, FullScreenMode.Windowed); SetupScene(); SetupLights(); SetupCameras(); SetupCornellBox(); raytracer = new Raytracer(scene, Color.black, mainCamera); RaytraceScene(); }
public bool MustInterrupt(Color c, TraceData traceData) { if (StopOnOverwhite && Raytracer.IsOverwhite(c)) { traceData.Counters.Overwhites++; return(true); } else { return(false); } }
protected override void Render() { if (isResizing) { return; } commandList.Begin(); if (DrawModeCpu) { if (!renderOnce) { Raytracer.RenderCpuNew(); } if (!isResizing) { unsafe { fixed(RgbaFloat *pixelDataPtr = Raytracer.FrameBuffer) { GraphicsDevice.UpdateTexture(transferTexture, (IntPtr)pixelDataPtr, transferTexture.Width * transferTexture.Height * (uint)sizeof(RgbaFloat), 0, 0, 0, transferTexture.Width, transferTexture.Height, 1, 0, 0); } } } } else { RenderGpu(); } commandList.SetFramebuffer(GraphicsDevice.MainSwapchain.Framebuffer); commandList.SetPipeline(graphicsPipeline); commandList.SetGraphicsResourceSet(0, graphicsSet); commandList.Draw(3); commandList.End(); if (!DrawModeCpu) { var rayCountView = GraphicsDevice.Map <uint>(rayCountReadback, MapMode.Read); var rPF = rayCountView[0]; GraphicsDevice.Unmap(rayCountReadback); } base.Render(); }
public override Vector3 GetIntensity(Ray ray, Intersection intersection, Scene scene) { //Calculate vector from intersection point to light point Vector3 lightvec = this.position - intersection.location; //If the best effect of the lightsource on the intersectionpoint is less than half the least visible difference if (lightvec.LengthSquared > maxIntensity * 512) { return(Vector3.Zero); } Vector3 lightnormal = Vector3.Normalize(lightvec); // The light is not inside the cone of angle cosangle if (Vector3.Dot(-lightnormal, direction) < cosangle) { return(Vector3.Zero); } float dot = Vector3.Dot(intersection.normal, lightnormal); // Is the light source on the same side as the normal? if (dot <= 0f) { return(Vector3.Zero); // In this case not. } // check the intersection as late as possible: if (scene.DoesIntersect(new Ray(intersection.location, lightnormal), lightvec.Length)) { return(Vector3.Zero); } if (ray.debug) { // Add a shadow ray to the debug screen Ray debugray = new Ray(intersection.location, lightnormal); debugray.debugSurface = ray.debugSurface; debugray.camerapos = ray.camerapos; debugray.debugxunit = ray.debugxunit; debugray.debugyunit = ray.debugyunit; Intersection fakeintersection = new Intersection(this.position, 0, null, Vector3.UnitX); Raytracer.DrawRayDebug(debugray, fakeintersection, 0x0000ff); } // Use inverse square law for intensity at distance lightvec.Length return(intensity * dot / lightvec.LengthSquared); }
private static void Main(string [] args) { RenderScene Scene = new RenderScene( ); Sphere S = new Sphere { Position = new Vector3(0, 0, -40), Radius = 1 }; Scene.AddRendererObject(S); RenderSettings Settings = new RaytracerSettings( ); Renderer R = new Raytracer( ); R.SetData(Scene, Settings); RendererImage Render = R.Render( ); Bitmap Bmp = Render.ConvertToBitmap( ); Bmp.Save("D:\\Temp\\tempimg.png"); Process.Start("D:\\Temp\\tempimg.png"); Console.ReadLine( ); }
public virtual RgbColor StartFromBackground(Ray ray, RgbColor initialWeight, float pdf) { isOnLightSubpath = true; // Find the first actual hitpoint on scene geometry var hit = scene.Raytracer.Trace(ray); if (!hit) { return(OnInvalidHit(ray, pdf, initialWeight, 1)); } // Sample the next direction (required to know the reverse pdf) var(pdfNext, pdfReverse, weight, direction) = SampleNextDirection(hit, ray, initialWeight, 1); // Both pdfs have unit sr-1 float pdfFromAncestor = pdf; float pdfToAncestor = pdfReverse; RgbColor estimate = OnHit(ray, hit, pdfFromAncestor, initialWeight, 1, 1.0f); OnContinue(pdfToAncestor, 1); // Terminate if the maximum depth has been reached if (maxDepth <= 1) { return(estimate); } // Terminate absorbed paths and invalid samples if (pdfNext == 0 || weight == RgbColor.Black) { return(estimate); } // Continue the path with the next ray ray = Raytracer.SpawnRay(hit, direction); return(estimate + ContinueWalk(ray, hit, pdfNext, initialWeight * weight, 2)); }
static void MeasurePinvokeOverhead(int numTrials) { var vertices = new Vector3[] { new Vector3(-1, 0, -1), new Vector3(1, 0, -1), new Vector3(1, 0, 1), new Vector3(-1, 0, 1) }; var indices = new int[] { 0, 1, 2, 0, 2, 3 }; TriangleMesh mesh = new TriangleMesh(vertices, indices); var rt = new Raytracer(); rt.AddMesh(mesh); rt.CommitScene(); Stopwatch stop = Stopwatch.StartNew(); for (int k = 0; k < numTrials; ++k) { for (int i = 0; i < 1000000; ++i) { rt.Trace(new Ray { Origin = new Vector3(-0.5f, -10, 0), Direction = new Vector3(0, 1, 0), MinDistance = 1.0f }); } } stop.Stop(); Console.WriteLine($"One million rays intersected in {stop.ElapsedMilliseconds / numTrials}ms"); }
public Application(Surface sur) { raytracer = new Raytracer(sur); camera = raytracer.camera; prevState = Keyboard.GetState(); }
void Start() { raytracer = GetComponent <Raytracer> (); }
public Application(Surface sur) { Screen = sur; R = new Raytracer(sur); C = Camera.Instance(); }
private static byte[] Lab04Textures() { Raytracer raytracer = new Raytracer(Premade.Lab04Textures.Camera(), Premade.Lab04Textures.Scene()); return(raytracer.CalculatePixelsByteArray(imageResolution, imageResolution)); }
private static byte[] Lab04BVH() { Raytracer raytracer = new Raytracer(Premade.Lab04BVH.Camera(), Premade.Lab04BVH.Scene(Scene.AccelerationStructure.BVH)); return(raytracer.CalculatePixelsByteArray(imageResolution, imageResolution)); }
protected override void Update(InputSnapshot snapshot, float elapsedSeconds) { base.Update(snapshot, elapsedSeconds); var frameRate = 1f / ElapsedSecondsBetweenRenders; var tickRate = 1f / elapsedSeconds; var rate = Raytracer.RaysPerFrame * frameRate; var millionRate = rate / 1_000_000; var budget = Stopwatch.Frequency / TargetTicksPerFrame; Window.Title = $"{millionRate:F2} MRays / sec. | {Raytracer.RaysPerFrame / 1_000_000:F2} MRays / Frame | {frameRate:F2} FPS | {tickRate:F2} TPS | {MillisecondsPerLogic:F0}ms / Update | {MillisecondsPerRender:F0}ms / Render | Budget: {budget:F0}ms / Frame"; if (ImGui.Begin("Debug")) { if (ImGui.BeginMenu("Raytracer Debug")) { if (ImGui.Button("Clear Framebuffer")) { Array.Clear(Raytracer.FrameBuffer, 0, Raytracer.FrameBuffer.Length); } ImGui.Checkbox("Render once on button press", ref renderOnce); if (renderOnce && ImGui.Button( renderThread != null && !renderThread.IsCompleted && !renderThread.IsCanceled ? "Rendering" : "Render Once")) { if (renderThread != null && !renderThread.IsCompleted && !renderThread.IsCanceled) { cancellationTokenSource?.Cancel(); } if (renderThread == null || renderThread.IsCompleted || renderThread.IsCanceled) { cancellationTokenSource = new CancellationTokenSource(100); renderThread = Task.Run(Raytracer.RenderCpuNew, cancellationTokenSource.Token); } } } } foreach (var keyEvent in snapshot.KeyEvents) { if (keyEvent.Down && keyEvent.Key == Key.D) { Raytracer.SceneParams.Camera.Origin.X += 1f; Raytracer.SceneParams.Camera.LookAt.X += 1f; Camera.Recalculate(ref Raytracer.SceneParams.Camera); } else if (keyEvent.Down && keyEvent.Key == Key.A) { Raytracer.SceneParams.Camera.Origin.X -= 1f; Raytracer.SceneParams.Camera.LookAt.X -= 1f; Camera.Recalculate(ref Raytracer.SceneParams.Camera); } } Raytracer.RenderSettings(); }
RgbColor ContinueWalk(Ray ray, SurfacePoint previousPoint, float pdfDirection, RgbColor throughput, int depth) { // Terminate if the maximum depth has been reached if (depth >= maxDepth) { OnTerminate(); return(RgbColor.Black); } var hit = scene.Raytracer.Trace(ray); if (!hit) { var result = OnInvalidHit(ray, pdfDirection, throughput, depth); OnTerminate(); return(result); } // Convert the PDF of the previous hemispherical sample to surface area float pdfFromAncestor = pdfDirection * SampleWarp.SurfaceAreaToSolidAngle(previousPoint, hit); // Geometry term might be zero due to, e.g., shading normal issues // Avoid NaNs in that case by terminating early if (pdfFromAncestor == 0) { OnTerminate(); return(RgbColor.Black); } RgbColor estimate = OnHit(ray, hit, pdfFromAncestor, throughput, depth, SampleWarp.SurfaceAreaToSolidAngle(hit, previousPoint)); // Terminate with Russian roulette float survivalProb = ComputeSurvivalProbability(hit, ray, throughput, depth); if (rng.NextFloat() > survivalProb) { OnTerminate(); return(estimate); } // Continue based on the splitting factor int numSplits = ComputeSplitFactor(hit, ray, throughput, depth); for (int i = 0; i < numSplits; ++i) { // Sample the next direction and convert the reverse pdf var(pdfNext, pdfReverse, weight, direction) = SampleNextDirection(hit, ray, throughput, depth); float pdfToAncestor = pdfReverse * SampleWarp.SurfaceAreaToSolidAngle(hit, previousPoint); if (pdfToAncestor == 0) { Debug.Assert(pdfNext == 0); } OnContinue(pdfToAncestor, depth); if (pdfNext == 0 || weight == RgbColor.Black) { OnTerminate(); continue; } // Account for splitting and roulette in the weight weight *= 1.0f / (survivalProb * numSplits); // Continue the path with the next ray var nextRay = Raytracer.SpawnRay(hit, direction); estimate += ContinueWalk(nextRay, hit, pdfNext, throughput * weight, depth + 1); } return(estimate); }
} // It is here to allow enable/disable component. public abstract Color GetColor(Raytracer raytracer, Ray ray, RaycastHit hit, TraceData traceData);
// initialize public void Init() { rayTracer = new Raytracer(); c = new Camera(Vector3.Zero, new Vector3(0, 0, 1)); c.Screen(); }
public static void MeasurePinvokeOverhead(int numTrials) { var vertices = new Vector3[] { new Vector3(-1, 0, -1), new Vector3(1, 0, -1), new Vector3(1, 0, 1), new Vector3(-1, 0, 1) }; var indices = new int[] { 0, 1, 2, 0, 2, 3 }; TriangleMesh mesh = new TriangleMesh(vertices, indices); var rt = new Raytracer(); rt.AddMesh(mesh); rt.CommitScene(); Random rng = new(1337); Stopwatch stop = Stopwatch.StartNew(); for (int k = 0; k < numTrials; ++k) { for (int i = 0; i < 1000000; ++i) { rt.Trace(new Ray { Origin = new Vector3( (float)rng.NextDouble(), (float)rng.NextDouble(), (float)rng.NextDouble()), Direction = new Vector3( (float)rng.NextDouble(), (float)rng.NextDouble(), (float)rng.NextDouble()), MinDistance = 0.0f }); } } stop.Stop(); Console.WriteLine($"One million rays intersected in {stop.ElapsedMilliseconds / numTrials}ms"); long traceCost = stop.ElapsedMilliseconds / numTrials; stop = Stopwatch.StartNew(); for (int k = 0; k < numTrials; ++k) { for (int i = 0; i < 1000000; ++i) { new Vector3( (float)rng.NextDouble(), (float)rng.NextDouble(), (float)rng.NextDouble() ); new Vector3( (float)rng.NextDouble(), (float)rng.NextDouble(), (float)rng.NextDouble() ); } } stop.Stop(); Console.WriteLine($"RNG overhead: {stop.ElapsedMilliseconds / numTrials}ms"); long rngCost = stop.ElapsedMilliseconds / numTrials; Console.WriteLine($"Pure cost for tracing + overhead: {traceCost-rngCost}ms"); }
public override Vector3 GetIntensity(Ray ray, Intersection intersection, Scene scene) { float diff = 0f, spec = 0f; Material mat = intersection.primitive.material; bool visible = false; // Calculate vector from intersection point to light point Vector3 L = position - intersection.location; // Use inverse square law for intensity at a certain distance float dist = L.Length, distSq = L.LengthSquared; // If the best effect of the lightsource on the intersectionpoint is less than half the least visible difference if (distSq <= maxIntensity * 512) { L /= dist; float NdotL = Vector3.Dot(intersection.normal, L); // Is the light direction on the same side as the normal? if (NdotL > 0f) { // check the intersection as late as possible: visible = !scene.DoesIntersect(new Ray(intersection.location, L), dist); if (visible) { diff = NdotL / distSq; if (ray.debug) { // Add a shadow ray to the debug screen Ray debugray = new Ray(intersection.location, L); debugray.debugSurface = ray.debugSurface; debugray.camerapos = ray.camerapos; debugray.debugxunit = ray.debugxunit; debugray.debugyunit = ray.debugyunit; // Fake intersection makes sure the line is drawn from the intersection point up to the light position Intersection fakeintersection = new Intersection(this.position, 0, null, Vector3.UnitX); Raytracer.DrawRayDebug(debugray, fakeintersection, 0x0000ff); } } } // Add some specularity if (mat.isSpecular) { Vector3 viewDir = -ray.direction; Vector3 halfway = (L + viewDir).Normalized(); float NdotH = Vector3.Dot(intersection.normal, halfway); // Are we on the good side of the normal? if (NdotH > 0f) { // Check if this light is visible from the intersection (calculate if not calculated already) if (NdotL <= 0f) { visible = !scene.DoesIntersect(new Ray(intersection.location, L), dist); } if (visible) { // Use a power law to let this hardness have on effect on the decay of intensity of the white effect spec = ((float)Math.Pow(NdotH, mat.hardness)) / distSq; } } } } // mix the colors, specularity (from light, not reflection) is always white. Vector3 diffuse = intersection.primitive.GetDiffuseColor(intersection); return(intensity * (diffuse * diff + Vector3.One * spec)); }
public override Color GetColor(Raytracer raytracer, Ray ray, RaycastHit hit, TraceData traceData) { Vector2 texCoord; Vector3 surfaceNormal; Vector3 tangent, binormal; bool texCoordAndTangentRequired = NormalMap != null || DiffuseTexture != null || SpecularMap != null || ReflectionMap != null; texCoordAndTangentRequired = true; // DEBUG if (texCoordAndTangentRequired) { Vector3 localNormal; Vector3 localTangent; ColliderUtils.GetTexCoordAndTangent( hit, out texCoord, out localNormal, out localTangent ); tangent = hit.collider.transform.TransformDirection(localTangent); surfaceNormal = hit.collider.transform.TransformDirection(localNormal); binormal = Vector3.Cross(tangent, surfaceNormal); #region Debug Visualisation #if DEBUG_SHOW_TEX_COORDS return(new Color(texCoord.x, texCoord.y, 0, 1)); #endif #if DEBUG_SHOW_BARYCENTRIC_COORDS Vector3 dbgBc = hit.barycentricCoordinate; return(new Color(dbgBc.x, dbgBc.y, dbgBc.z, 1)); #endif #if DEBUG_SHOW_NORMALS Vector3 dbgLocalNormal = localNormal; //dbgLocalNormal = new Vector3 ( // Mathf.Abs ( dbgLocalNormal.x ), // Mathf.Abs ( dbgLocalNormal.y ), // Mathf.Abs ( dbgLocalNormal.z ) //); dbgLocalNormal = (dbgLocalNormal + Vector3.one) * 0.5f; return(new Color(dbgLocalNormal.x, dbgLocalNormal.y, dbgLocalNormal.z, 1)); #endif #if DEBUG_SHOW_TANGENTS Vector3 dbgLocalTangent = localTangent; if (false) { dbgLocalTangent = new Vector3( Mathf.Abs(dbgLocalTangent.x), Mathf.Abs(dbgLocalTangent.y), Mathf.Abs(dbgLocalTangent.z) ); } else if (false) { dbgLocalTangent = new Vector3( Mathf.Abs(dbgLocalTangent.x > 0 ? dbgLocalTangent.x : 0), Mathf.Abs(dbgLocalTangent.y > 0 ? dbgLocalTangent.y : 0), Mathf.Abs(dbgLocalTangent.z > 0 ? dbgLocalTangent.z : 0) ); } else { dbgLocalTangent = (dbgLocalTangent + Vector3.one) * 0.5f; } return(new Color(dbgLocalTangent.x, dbgLocalTangent.y, dbgLocalTangent.z, 1)); #endif #if DEBUG_SHOW_BINORMALS Vector3 localBinormal = Vector3.Cross(localTangent, localNormal); Vector3 dbgBinormal = localBinormal; dbgBinormal = new Vector3( Mathf.Abs(dbgBinormal.x), Mathf.Abs(dbgBinormal.y), Mathf.Abs(dbgBinormal.z) ); return(new Color(dbgBinormal.x, dbgBinormal.y, dbgBinormal.z, 1)); #endif #endregion Debug Visualisation } else { texCoord = Vector2.zero; surfaceNormal = hit.normal; tangent = Vector3.zero; binormal = Vector3.zero; } bool entering = Vector3.Dot(hit.normal, ray.direction) <= 0; surfaceNormal = entering ? surfaceNormal : -surfaceNormal; /* TODO: revise where "entering" calculated upon hit.normal should be replaced * with "entering" calculated upon surfaceNormal transformed with TBN. */ if (NormalMap != null && NormalMapInfluence > 0) { var normalMapRt = TextureCache.FromUnityTexture(NormalMap); Color normalMapColor = normalMapRt.GetFilteredPixel(texCoord.x, texCoord.y); Vector3 texNormal = new Vector3(normalMapColor.r, normalMapColor.g, normalMapColor.b); texNormal = 2 * texNormal - Vector3.one; texNormal.Normalize(); Vector3 texNormalWorld = Raytracer.TransformTbn(texNormal, tangent, binormal, surfaceNormal); float normalMapInfluence = Mathf.Clamp01(NormalMapInfluence); surfaceNormal = Vector3.Lerp(surfaceNormal, texNormalWorld, normalMapInfluence).normalized; #if DEBUG_SHOW_SURFACE_NORMALS Vector3 dbgSurfaceNormal = surfaceNormal; //dbgSurfaceNormal = new Vector3 ( // Mathf.Abs ( dbgSurfaceNormal.x ), // Mathf.Abs ( dbgSurfaceNormal.y ), // Mathf.Abs ( dbgSurfaceNormal.z ) //); dbgSurfaceNormal = (dbgSurfaceNormal + Vector3.one) * 0.5f; return(new Color(dbgSurfaceNormal.x, dbgSurfaceNormal.y, dbgSurfaceNormal.z, 1)); #endif } float specularIntensity = SpecularComponent; if (SpecularMap != null && SpecularMapInfluence > 0 && specularIntensity > 0) { var specularMapRt = TextureCache.FromUnityTexture(SpecularMap); Color specularMapColor = specularMapRt.GetFilteredPixel(texCoord.x, texCoord.y); specularIntensity *= specularMapColor.grayscale * specularMapColor.a; float specularMapInfluence = Mathf.Clamp01(SpecularMapInfluence); specularIntensity = Mathf.Lerp(SpecularComponent, specularIntensity, specularMapInfluence); } Color totalColor = Color.black; Color diffuseLightSumColor = raytracer.OverrideAmbientLight ? raytracer.AmbientLight : RenderSettings.ambientLight; diffuseLightSumColor *= DiffuseComponent; Color specularLightSumColor = Color.black; var lights = Light.GetLights(LightType.Point, 0); foreach (var light in lights) { Vector3 vToLight = light.transform.position - hit.point; float lightVolumeRadius = light.range * 0.00625f; // Empirical coefficient. float distance = vToLight.magnitude - lightVolumeRadius; if (distance < 0) { distance = 0; } else if (distance >= light.range) { continue; } Vector3 dirToLight = vToLight.normalized; float attenuation; attenuation = 1 - distance / light.range; attenuation = attenuation * attenuation; float lightIntensity = light.intensity * LightIntensityFactor; if (DiffuseComponent > 0) { float diffuseIntensity = Vector3.Dot(dirToLight, surfaceNormal); if (diffuseIntensity > 0) { diffuseIntensity = Mathf.Pow(diffuseIntensity, DiffuseExponent); Color diffuseLightColor = light.color * attenuation * diffuseIntensity * lightIntensity; diffuseLightSumColor += diffuseLightColor; } } if (specularIntensity > 0) { Vector3 reflectionDir = Vector3.Reflect(-dirToLight, surfaceNormal); Vector3 vToView = raytracer.Camera.transform.position - hit.point; Vector3 dirToView = vToView.normalized; float specularity = Vector3.Dot(reflectionDir, dirToView); if (specularity > 0) { specularity = Mathf.Pow(specularity, SpecularPower); Color specularLightColor = light.color * attenuation * specularity * lightIntensity; specularLightSumColor += specularLightColor; } } } Color diffuseColor; if (DiffuseTexture != null) { var diffuseTextureRt = TextureCache.FromUnityTexture(DiffuseTexture); // TODO: calculate miplevel, get the color according to its value. Color texColor = diffuseTextureRt.GetFilteredPixel(texCoord.x, texCoord.y); if (texColor.a < 1 && DiffuseColorIsBackground) { diffuseColor = Color.Lerp(this.DiffuseColor, texColor, texColor.a); } else { diffuseColor = Color.Lerp(Color.black, texColor, texColor.a); } diffuseColor.a = texColor.a; } else { diffuseColor = this.DiffuseColor; } totalColor = diffuseLightSumColor * diffuseColor * DiffuseComponent + specularLightSumColor * specularIntensity; if (raytracer.MustInterrupt(totalColor, traceData)) { return(totalColor); } bool willReflect; float reflectionIntensity; if (entering) { willReflect = ReflectionComponent > 0 && traceData.NumReflections < raytracer.MaxReflections; reflectionIntensity = ReflectionComponent; } else { willReflect = InnerReflectionComponent > 0 && traceData.NumInnerReflections < raytracer.MaxInnerReflections; reflectionIntensity = InnerReflectionComponent; } if (willReflect && ReflectionMap != null && ReflectionMapInfluence > 0) { var reflectionMapRt = TextureCache.FromUnityTexture(ReflectionMap); Color reflectionMapColor = reflectionMapRt.GetFilteredPixel(texCoord.x, texCoord.y); float reflectionMapIntensity = reflectionMapColor.grayscale * reflectionMapColor.a; float reflectionMapInfluence = Mathf.Clamp01(ReflectionMapInfluence); reflectionIntensity = Mathf.Lerp(reflectionIntensity, reflectionMapIntensity * reflectionIntensity, reflectionMapInfluence); willReflect = reflectionIntensity > 0; } float refractionIntensity = RefractionComponent; if (RefractWhereTranslucent) { refractionIntensity *= 1 - diffuseColor.a * DiffuseComponent; } bool willRefract = refractionIntensity > 0 && traceData.NumRefractions < raytracer.MaxRefractions; bool forkingRequired = willReflect && willRefract; TraceData tdForRefraction; if (forkingRequired) { tdForRefraction = traceData.Fork(); } else { tdForRefraction = traceData; } if (willReflect) { if (entering) { traceData.NumReflections++; traceData.Counters.Reflections++; } else { traceData.NumInnerReflections++; traceData.Counters.InnerReflections++; } Vector3 reflectionDir = Vector3.Reflect(ray.direction, surfaceNormal); Vector3 pushedOutPoint = hit.point + reflectionDir * Raytracer.PushOutMagnitude; Color reflectionColor = raytracer.Trace(new Ray(pushedOutPoint, reflectionDir), traceData); totalColor += reflectionColor * reflectionIntensity; if (raytracer.MustInterrupt(totalColor, traceData)) { return(totalColor); } } if (willRefract) { tdForRefraction.NumRefractions++; tdForRefraction.Counters.Refractions++; CoefficientOut = RefractionIndex; CoefficientIn = 1 / RefractionIndex; if (CoefficientOut > 1) { CriticalOutAngleCos = Mathf.Sqrt(1 - CoefficientIn * CoefficientIn); CriticalInAngleCos = 0; } else { CriticalOutAngleCos = 0; CriticalInAngleCos = Mathf.Sqrt(1 - CoefficientOut * CoefficientOut); } float criticalAngleCos = entering ? CriticalInAngleCos : CriticalOutAngleCos; Vector3 refractionDir; float nDotRay = Vector3.Dot(surfaceNormal, ray.direction); if (Mathf.Abs(nDotRay) >= criticalAngleCos) { if (entering) { tdForRefraction.PenetrationStack.Push(hit); } else { tdForRefraction.PenetrationStack.Pop(); } float k = entering ? CoefficientIn : CoefficientOut; refractionDir = Raytracer.Refract(ray.direction, surfaceNormal, nDotRay, k); refractionDir.Normalize(); } else // Total internal reflection. { refractionDir = Vector3.Reflect(ray.direction, surfaceNormal); } Vector3 pushedOutPoint = hit.point + refractionDir * Raytracer.PushOutMagnitude; Color refractionColor = raytracer.Trace(new Ray(pushedOutPoint, refractionDir), tdForRefraction); if (ColorAberration != 0 && entering) { //float rDotRay = Vector3.Dot ( refractionDir, ray.direction ); refractionColor = HsvColor.ChangeHue(refractionColor, (1 + nDotRay) * ColorAberration); } totalColor += refractionColor * refractionIntensity; if (raytracer.MustInterrupt(totalColor, tdForRefraction)) { return(totalColor); } } return(totalColor); }
public static void ComplexScene(int numTrials) { // the scene is compressed to avoid git issues if (!File.Exists("../Data/breakfast_room.obj")) { ZipFile.ExtractToDirectory("../Data/breakfast_room.zip", "../Data"); } Stopwatch stop = Stopwatch.StartNew(); List <TriangleMesh> meshes = new(); Vector3 min = Vector3.One * float.MaxValue; Vector3 max = -Vector3.One * float.MaxValue; Assimp.AssimpContext context = new(); var scene = context.ImportFile("../Data/breakfast_room.obj", Assimp.PostProcessSteps.GenerateNormals | Assimp.PostProcessSteps.JoinIdenticalVertices | Assimp.PostProcessSteps.PreTransformVertices | Assimp.PostProcessSteps.Triangulate); foreach (var m in scene.Meshes) { var material = scene.Materials[m.MaterialIndex]; string materialName = material.Name; Vector3[] vertices = new Vector3[m.VertexCount]; for (int i = 0; i < m.VertexCount; ++i) { vertices[i] = new(m.Vertices[i].X, m.Vertices[i].Y, m.Vertices[i].Z); } meshes.Add(new(vertices, m.GetIndices())); min.X = MathF.Min(min.X, m.BoundingBox.Min.X); min.Y = MathF.Min(min.X, m.BoundingBox.Min.Y); min.Z = MathF.Min(min.X, m.BoundingBox.Min.Z); max.X = MathF.Max(max.X, m.BoundingBox.Max.X); max.Y = MathF.Max(max.X, m.BoundingBox.Max.Y); max.Z = MathF.Max(max.X, m.BoundingBox.Max.Z); } var diagonal = max - min; Console.WriteLine($"Scene loaded in {stop.ElapsedMilliseconds}ms"); stop.Restart(); var rt = new Raytracer(); foreach (var m in meshes) { rt.AddMesh(m); } rt.CommitScene(); Console.WriteLine($"Acceleration structures built in {stop.ElapsedMilliseconds}ms"); Random rng = new(1337); Vector3 NextVector() => new Vector3( (float)rng.NextDouble(), (float)rng.NextDouble(), (float)rng.NextDouble()); stop.Restart(); float averageDistance = 0; for (int k = 0; k < numTrials; ++k) { for (int i = 0; i < 1000000; ++i) { var hit = rt.Trace(new Ray { Origin = NextVector() * diagonal + min, Direction = NextVector(), MinDistance = 0.0f }); if (hit) { averageDistance += hit.Distance / 1000000 / numTrials; } } } stop.Stop(); Console.WriteLine($"One million closest hits found in {stop.ElapsedMilliseconds / numTrials}ms"); Console.WriteLine($"Average distance: {averageDistance}"); stop.Restart(); float averageVisibility = 0; for (int k = 0; k < numTrials; ++k) { for (int i = 0; i < 1000000; ++i) { bool occluded = rt.IsOccluded(new ShadowRay(new Ray { Origin = NextVector() * diagonal + min, Direction = NextVector(), MinDistance = 0.0f }, maxDistance: (float)rng.NextDouble() * averageDistance * 5)); if (!occluded) { averageVisibility += 1.0f / 1000000.0f / numTrials; } } } stop.Stop(); Console.WriteLine($"One million any hits found in {stop.ElapsedMilliseconds / numTrials}ms"); Console.WriteLine($"Average visibility: {averageVisibility}"); }