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
        }
Esempio n. 2
0
        public bool Scatter(Ray ray, HitRecord rec, ref Random rng, PerlinData perlinData,
                            out float3 reflectance, out Ray scattered)
        {
            switch (Type)
            {
            case MaterialType.Lambertian:
            {
                reflectance = Texture.Value(rec.Point, rec.Normal, TextureScale, perlinData);
                float3 randomDirection = rng.OnCosineWeightedHemisphere(rec.Normal);
                scattered = new Ray(rec.Point, randomDirection, ray.Time);
                return(true);
            }

            case MaterialType.Metal:
            {
                float3 outgoingDirection = WorldToTangentSpace(-ray.Direction, rec.Normal);

                if (GgxMicrofacet.ImportanceSample(Texture.Value(rec.Point, rec.Normal, TextureScale, perlinData),
                                                   Roughness, ref rng, outgoingDirection, out float3 toLight, out reflectance))
                {
                    float3 scatterDirection = TangentToWorldSpace(toLight, rec.Normal);
                    scattered = new Ray(rec.Point, scatterDirection, ray.Time);
                    return(true);
                }

                scattered = default;
                return(false);
            }

            case MaterialType.Dielectric:
            {
                float3 reflected = reflect(ray.Direction, rec.Normal);
                reflectance = 1;
                float  niOverNt;
                float3 outwardNormal;
                float  cosine;

                if (dot(ray.Direction, rec.Normal) > 0)
                {
                    outwardNormal = -rec.Normal;
                    niOverNt      = RefractiveIndex;
                    cosine        = RefractiveIndex * dot(ray.Direction, rec.Normal);
                }
                else
                {
                    outwardNormal = rec.Normal;
                    niOverNt      = 1 / RefractiveIndex;
                    cosine        = -dot(ray.Direction, rec.Normal);
                }

                if (Refract(ray.Direction, outwardNormal, niOverNt, out float3 refracted))
                {
                    float reflectProb = Schlick(cosine, RefractiveIndex);
                    scattered = new Ray(rec.Point, rng.NextFloat() < reflectProb ? reflected : refracted, ray.Time);
                }
                else
                {
                    scattered = new Ray(rec.Point, reflected, ray.Time);
                }

                return(true);
            }

            case MaterialType.ProbabilisticVolume:
                scattered   = new Ray(rec.Point, rng.NextFloat3Direction());
                reflectance = Texture.Value(rec.Point, rec.Normal, TextureScale, perlinData);
                return(true);

            default:
                reflectance = default;
                scattered   = default;
                return(false);
            }
        }
        // iterative entity array hit test
        public static bool Hit(this NativeArray <Entity> entities, Ray r, float tMin, float tMax, ref Random rng, out HitRecord rec)
        {
            bool hitAnything = false;

            rec = new HitRecord(tMax, 0, 0, default);

            for (var i = 0; i < entities.Length; i++)
            {
                if (entities[i].Hit(r, tMin, rec.Distance, ref rng, out HitRecord thisRec))
                {
                    hitAnything = true;
                    rec         = thisRec;
                }
            }

            return(hitAnything);
        }
Esempio n. 4
0
        public unsafe void Sample(Ray materialScatterRay, float3 outgoingLightDirection, HitRecord rec, Material material, ref Random rng,
                                  out Ray scatterRay, out float pdfValue, out int?targetEntityId)
        {
            int totalOptions = TargetEntities.Length + (Mode == ImportanceSamplingMode.Mixture ? 1 : 0);
            int chosenOption = rng.NextInt(0, totalOptions);

            if (chosenOption == TargetEntities.Length)
            {
                scatterRay     = materialScatterRay;
                targetEntityId = null;
            }
            else
            {
                Entity *chosenEntity  = (Entity *)TargetEntities.GetUnsafeReadOnlyPtr() + chosenOption;
                float3  pointOnEntity = chosenEntity->RandomPoint(materialScatterRay.Time, ref rng);
                scatterRay = new Ray(materialScatterRay.Origin,
                                     normalize(pointOnEntity - materialScatterRay.Origin));
                targetEntityId = chosenEntity->Id;
            }

            pdfValue = 0;
            if (Mode == ImportanceSamplingMode.Mixture)
            {
                pdfValue += material.Pdf(scatterRay.Direction, outgoingLightDirection, rec.Normal);
            }

            var basePointer = (Entity *)TargetEntities.GetUnsafeReadOnlyPtr();

            for (int i = 0; i < TargetEntities.Length; i++)
            {
                pdfValue += (basePointer + i)->Pdf(scatterRay, ref rng);
            }
            pdfValue /= totalOptions;
        }