protected override void Raycast(ref State state, ref TriangleSoA container) { Ray ray = state.ray; float minDist = state.minDist, maxDist = state.maxDist; int rayIndex = state.rayIndex; HitInfo[] hits = state.hits; HitInfo hitInfo = new HitInfo(); for (int i = 0; i < container.objectCount; i++) { // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm Vector3 e1, e2; Vector3 p1 = container.p1[i]; Vector3 p2 = container.p2[i]; Vector3 p3 = container.p3[i]; e1 = p2 - p1; e2 = p3 - p1; Vector3 h = Vector3.Cross(ray.direction, e2); float a = Vector3.Dot(e1, h); if (a > -Epsilon && a < Epsilon) { continue; } float f = 1.0f / a; Vector3 s = ray.origin - p1; float u = f * Vector3.Dot(s, h); if (u < 0 || u > 1) { continue; } Vector3 q = Vector3.Cross(s, e1); float v = f * Vector3.Dot(ray.direction, q); if (v < 0 || (u + v) > 1) { continue; } float t = f * Vector3.Dot(e2, q); if (t > minDist && t <= maxDist) { hitInfo.distance = t; hitInfo.material = container.material[i]; ray.GetPointOptimized(t, ref hitInfo.point); // Barycentric normal Vector3 v0 = p2 - p1, v1 = p3 - p1, v2 = hitInfo.point - p1; float d00 = Vector3.Dot(v0, v0); float d01 = Vector3.Dot(v0, v1); float d11 = Vector3.Dot(v1, v1); float d20 = Vector3.Dot(v2, v0); float d21 = Vector3.Dot(v2, v1); float denom = d00 * d11 - d01 * d01; float baryV = (d11 * d20 - d01 * d21) / denom; float baryW = (d00 * d21 - d01 * d20) / denom; float baryU = 1.0f - baryV - baryW; hitInfo.normal = container.n1[i] * baryU + container.n2[i] * baryV + container.n3[i] * baryW; if (BitHelper.GetBit(ref state.hitMask, rayIndex)) { HitInfo.ExchangeIfBetter(ref hits[rayIndex], hitInfo); } else { hits[rayIndex] = hitInfo; BitHelper.SetBit(ref state.hitMask, rayIndex); } } } }
protected override void Raycast(ref State state, ref SphereSoA container) { Ray ray = state.ray; float minDist = state.minDist, maxDist = state.maxDist; int rayIndex = state.rayIndex; HitInfo[] hits = state.hits; HitInfo hitInfo = new HitInfo(); for (int j = 0; j < container.objectCount; j++) { // Inlined and optimized math Vector3 center = container.center[j]; Vector3 oc = ray.origin - center; float b = Vector3.Dot(oc, ray.direction); if (b > 0f) // Behind? { continue; } bool hasHit = false; float c = oc.LengthSquared(); float discriminantSqr = b * b - (c - container.radiusSq[j]); if (discriminantSqr > 0) { float discriminant = Mathf.Sqrt(discriminantSqr); hitInfo.distance = (-b - discriminant); if (hitInfo.distance < maxDist && hitInfo.distance > minDist) { ray.GetPointOptimized(hitInfo.distance, ref hitInfo.point); hitInfo.normal = (hitInfo.point - center) * container.invRadius[j]; hitInfo.material = container.material[j]; hasHit = true; } else { hitInfo.distance = (-b + discriminant); if (hitInfo.distance < maxDist && hitInfo.distance > minDist) { ray.GetPointOptimized(hitInfo.distance, ref hitInfo.point); hitInfo.normal = (hitInfo.point - center) * container.invRadius[j]; hitInfo.material = container.material[j]; hasHit = true; } } } if (hasHit) { if (BitHelper.GetBit(ref state.hitMask, rayIndex)) { HitInfo.ExchangeIfBetter(ref state.hits[rayIndex], hitInfo); } else { hits[rayIndex] = hitInfo; BitHelper.SetBit(ref state.hitMask, rayIndex); } } } }
public static void TraceTile(TileTraceParameters tile, float[] backbuffer, out long rayCount) { InitBatchArray(ref _rays); InitBatchArray(ref _hits); InitBatchArray(ref _localColor); Ray[] rays = _rays; HitInfo[] hits = _hits; Vector3[] localColor = _localColor; uint rndState = FastRandom.Seed(); rayCount = 0; for (int x = tile.xStart; x < tile.xEnd; x++) { for (int y = tile.yStart; y < tile.yEnd; y++) { Vector3 color = new Vector3(0, 0, 0); // Sample bouncing batches for (int b = 0; b < tile.runtimeParams.sampleBatchCount; b++) { // Calculate batch info int batchStart = b * tile.runtimeParams.batchSize, batchEnd = batchStart + tile.runtimeParams.batchSize; int batchRayCount = Math.Min(batchEnd, tile.parameters.samplesPerPixel); batchRayCount -= batchStart; uint hitMask = 0; uint bounces = uint.MaxValue; float u = x * tile.runtimeParams.invWidth, v = y * tile.runtimeParams.invHeight; // Get initial rays tile.parameters.camera.GetRays(u, v, rays, batchRayCount, ref rndState); // Reset local colors for (int i = 0; i < batchRayCount; i++) { localColor[i] = tile.parameters.ambientLight; } Vector3 atten; int bounceCount = 0; while (bounceCount < tile.parameters.maxBounces && bounces != 0) { tile.parameters.scene.Raycast(rays, hits, 0.01f, tile.parameters.maxDepth, batchRayCount, bounces, ref hitMask, ref rayCount); if (hitMask == 0) { break; } bounces = 0; for (int i = 0; i < batchRayCount; i++) { if (BitHelper.GetBit(ref hitMask, i)) // Did ray hit? { if (hits[i].material.Scatter(ref hits[i], out atten, ref rays[i], tile.parameters.scene, ref rndState)) { BitHelper.SetBit(ref bounces, i); } localColor[i] *= atten; } } bounceCount++; } for (int i = 0; i < batchRayCount; i++) { color += localColor[i]; } } color *= tile.runtimeParams.invSamplesPerPixel; // Write backbuffer int idx = (y * (tile.parameters.width * 3)) + (x * 3); backbuffer[idx] = color.X; backbuffer[idx + 1] = color.Y; backbuffer[idx + 2] = color.Z; } } }