public static unsafe Sphere *intersect(ref Ray r, out double t) { t = 1e20; Sphere *ret = null; fixed(Sphere *pStart = &spheres[0]) { int sz = spheres.Length; for (Sphere *s = pStart; s != pStart + sz; ++s) { double d = s->intersect(ref r); if (d < t) { t = d; ret = s; } } } return(ret); }
static int DoSphereVsSpheres(Sphere *spheres, int numSpheres, float3 *centerPtr, float radius, out int numIntersections) { float3 center = *centerPtr; int first = numSpheres; int n = 0; for (int i = 0; i < numSpheres; i++) { float r = spheres[i].Radius + radius; if (math.distancesq(spheres[i].Position, center) < r * r) { if (i < first) { first = i; } n++; } } numIntersections = n; return(first == numSpheres ? -1 : first); }
public static unsafe Vec radiance(ref Ray r, int depth, ref RandomLCG rand) { double t; // distance to intersection Sphere *obj = intersect(ref r, out t); if (obj == null) { return(Vec_Zero); // if miss, return black } else { int newDepth = depth + 1; bool isMaxDepth = newDepth > 100; // Russian roulette for path termination bool isUseRR = newDepth > 5; bool isRR = isUseRR && rand.NextNumber() < obj->maxC; if (isMaxDepth || (isUseRR && !isRR)) { return(obj->e); } else { Vec f = (isUseRR && isRR) ? obj->cc : obj->c; var tmp1 = r.d.Mul(t); Vec x = r.o.Add(ref tmp1); Vec n = (x.Sub(ref obj->p)).Norm(); Vec nl = n.Dot(ref r.d) < 0 ? n : n.Mul(-1); if (obj->refl == MatType.DIFF) { // Ideal DIFFUSE reflection double r1 = 2 * M_PI * rand.NextNumber(); double r2 = rand.NextNumber(); double r2s = MathSqrt(r2); Vec w = nl; Vec wo = w.x <-0.1 || w.x> 0.1 ? Vec_YAxis : Vec_XAxis; Vec u = (wo.Cross(ref w)).Norm(); Vec v = w.Cross(ref u); var tmp2 = v.Mul(MathSin(r1)).Mul(r2s); var tmp3 = w.Mul(MathSqrt(1 - r2)); Vec d = (u.Mul(MathCos(r1)).Mul(r2s).Add(ref tmp2).Add(ref tmp3)).Norm(); var tmp4 = new Ray(ref x, ref d); var tmp5 = radiance(ref tmp4, newDepth, ref rand); var tmp6 = f.Mul(ref tmp5); return(obj->e.Add(ref tmp6)); } else if (obj->refl == MatType.SPEC) // Ideal SPECULAR reflection { var tmp8 = n.Mul(2 * (n.Dot(ref r.d))); var tmp9 = r.d.Sub(ref tmp8); var tmp7 = new Ray(ref x, ref tmp9); var tmp10 = radiance(ref tmp7, newDepth, ref rand); var tmp11 = f.Mul(ref tmp10); return(obj->e.Add(ref tmp11)); } else { // Ideal dielectric REFRACTION var tmp100 = n.Mul(2 * (n.Dot(ref r.d))); var tmp101 = r.d.Sub(ref tmp100); Ray reflRay = new Ray(ref x, ref tmp101); bool into = n.Dot(ref nl) > 0; // Ray from outside going in? double nc = 1; double nt = 1.5; double nnt = into ? nc / nt : nt / nc; double ddn = r.d.Dot(ref nl); double cos2t = 1 - nnt * nnt * (1 - ddn * ddn); if (cos2t < 0) // Total internal reflection { var tmp12 = radiance(ref reflRay, newDepth, ref rand); var tmp13 = f.Mul(ref tmp12); return(obj->e.Add(ref tmp13)); } else { var tmp14 = n.Mul((into ? 1 : -1) * (ddn * nnt + MathSqrt(cos2t))); Vec tdir = (r.d.Mul(nnt).Sub(ref tmp14)).Norm(); double a = nt - nc; double b = nt + nc; double R0 = (a * a) / (b * b); double c = 1 - (into ? -ddn : tdir.Dot(ref n)); double Re = R0 + (1 - R0) * c * c * c * c * c; double Tr = 1 - Re; double P = .25 + .5 * Re; double RP = Re / P; double TP = Tr / (1 - P); Vec result; if (newDepth > 2) { // Russian roulette and splitting for selecting reflection and/or refraction if (rand.NextNumber() < P) { result = radiance(ref reflRay, newDepth, ref rand).Mul(RP); } else { var tmp15 = new Ray(ref x, ref tdir); result = radiance(ref tmp15, newDepth, ref rand).Mul(TP); } } else { var tmp16 = new Ray(ref x, ref tdir); var tmp17 = radiance(ref tmp16, newDepth, ref rand).Mul(Tr); result = radiance(ref reflRay, newDepth, ref rand).Mul(Re).Add(ref tmp17); } var tmp18 = f.Mul(ref result); return(obj->e.Add(ref tmp18)); } } } } }
public static unsafe bool Hit(this BvhNode n, NativeArray <Entity> entities, Ray r, float tMin, float tMax, ref Random rng, AccumulateJob.WorkingArea wa, #if FULL_DIAGNOSTICS ref Diagnostics diagnostics, #endif out HitRecord rec) { int candidateCount = 0, nodeStackHeight = 1; BvhNode **nodeStackTail = wa.Nodes; Entity * candidateListTail = wa.Entities - 1, candidateListHead = wa.Entities; float3 rayInvDirection = rcp(r.Direction); *nodeStackTail = &n; while (nodeStackHeight > 0) { BvhNode *nodePtr = *nodeStackTail--; nodeStackHeight--; if (!nodePtr->Bounds.Hit(r.Origin, rayInvDirection, tMin, tMax)) { continue; } #if FULL_DIAGNOSTICS diagnostics.BoundsHitCount++; #endif if (nodePtr->IsLeaf) { *++candidateListTail = entities[nodePtr->EntityId]; candidateCount++; } else { *++nodeStackTail = nodePtr->Left; *++nodeStackTail = nodePtr->Right; nodeStackHeight += 2; } } #if FULL_DIAGNOSTICS diagnostics.CandidateCount = candidateCount; #endif if (candidateCount == 0) { rec = default; return(false); } #if BVH_SIMD // TODO: this is fully broken // skip SIMD codepath if there's only one if (candidateCount == 1) { return(candidateListHead->Hit(r, tMin, tMax, out rec)); } var simdSpheresHead = (Sphere4 *)wa.Vectors; int simdBlockCount = (int)ceil(candidateCount / 4.0f); float4 a = dot(r.Direction, r.Direction); int4 curId = int4(0, 1, 2, 3), hitId = -1; float4 hitT = tMax; Sphere4 *blockCursor = simdSpheresHead; Entity * candidateCursor = candidateListHead; int candidateIndex = 0; for (int i = 0; i < simdBlockCount; i++) { for (int j = 0; j < 4; j++) { if (candidateIndex < candidateCount) { Sphere *sphereData = candidateCursor->AsSphere; float3 center = sphereData->Center(r.Time); blockCursor->CenterX[j] = center.x; blockCursor->CenterY[j] = center.y; blockCursor->CenterZ[j] = center.z; blockCursor->SquaredRadius[j] = sphereData->SquaredRadius; ++candidateCursor; ++candidateIndex; } else { blockCursor->CenterX[j] = float.MaxValue; blockCursor->CenterY[j] = float.MaxValue; blockCursor->CenterZ[j] = float.MaxValue; blockCursor->SquaredRadius[j] = 0; } } float4 ocX = r.Origin.x - blockCursor->CenterX, ocY = r.Origin.y - blockCursor->CenterY, ocZ = r.Origin.z - blockCursor->CenterZ; float4 b = ocX * r.Direction.x + ocY * r.Direction.y + ocZ * r.Direction.z; float4 c = ocX * ocX + ocY * ocY + ocZ * ocZ - blockCursor->SquaredRadius; float4 discriminant = b * b - a * c; bool4 discriminantTest = discriminant > 0; if (any(discriminantTest)) { float4 sqrtDiscriminant = sqrt(discriminant); float4 t0 = (-b - sqrtDiscriminant) / a; float4 t1 = (-b + sqrtDiscriminant) / a; float4 t = select(t1, t0, t0 > tMin); bool4 mask = discriminantTest & t > tMin & t < hitT; hitId = select(hitId, curId, mask); hitT = select(hitT, t, mask); } curId += 4; ++blockCursor; } if (all(hitId == -1)) { rec = default; return(false); } float minDistance = cmin(hitT); int laneMask = bitmask(hitT == minDistance); int firstLane = tzcnt(laneMask); int closestId = hitId[firstLane]; Sphere *closestSphere = candidateListHead[closestId].AsSphere; float3 point = r.GetPoint(minDistance); rec = new HitRecord(minDistance, point, (point - closestSphere->Center(r.Time)) / closestSphere->Radius, closestSphere->Material); return(true); #else // iterative candidate tests (non-SIMD) bool anyHit = candidateListHead->Hit(r, tMin, tMax, ref rng, out rec); for (int i = 1; i < candidateCount; i++) { bool thisHit = candidateListHead[i].Hit(r, tMin, tMax, ref rng, out HitRecord thisRec); if (thisHit && (!anyHit || thisRec.Distance < rec.Distance)) { anyHit = true; rec = thisRec; } } if (anyHit) { return(true); } rec = default; return(false); #endif }
static int DoSphereVsSpheresSimd(Sphere *spheres, int numSpheres, float3 *center, float radius, out int numIntersections) { // YOUR SIMD CODE HERE numIntersections = 0; return(-1); }