public void Execute(int index)
    {
        int     hitIndex = index / ns;
        Vector3 point    = hits[hitIndex].point;

        //Debug.Log("bounce" + hits[hitIndex].bHit);
        //Debug.Log(hits[hitIndex].bHit + " " + sourceRay[index].color);
        if (hits[hitIndex].bHit == 0)
        {
            Color col;
            //if (JobGlobal.envColor == sourceRay[index].color)
            //    col = sourceRay[index].color;
            //else
            col = JobGlobal.envColor;// * sourceRay[index].color;
            bounceRay[index] = new jRay()
            {
                origin    = point,
                direction = sourceRay[index].direction,
                bAlive    = sourceRay[index].bAlive,
                color     = col
            };
        }
        else
        {
            //Vector3 normal = hits[hitIndex].normal;
            //Vector3 refl = JobGlobal.emit(sourceRay[index], hits[hitIndex], material.distance);
            //bounceRay[index] = new jRay()
            //{
            //    origin = point,
            //    direction = refl,
            //    bAlive = sourceRay[index].bAlive + 1,
            //    color = material.albedo * sourceRay[index].color
            //};
        }
    }
    public bool HitSphere(jRay r, float tMin, float tMax, jCommonMaterial mat, out jRay or, out float t)
    {
        or = new jRay()
        {
            bAlive = 0,
            color  = r.color * JobGlobal.envColor
        };
        t = tMax;
        Vector3 oc           = r.origin - center;
        float   a            = Vector3.Dot(r.direction, r.direction);
        float   b            = Vector3.Dot(oc, r.direction);
        float   c            = Vector3.Dot(oc, oc) - radius * radius;
        float   discriminant = b * b - a * c;

        if (discriminant <= 0)
        {
            return(false);
        }

        float temp = (-b - Mathf.Sqrt(b * b - a * c)) / a;

        if (temp < tMax && temp > tMin)
        {
            Vector3 p    = r.origin + temp * r.direction;
            Vector3 n    = (p - center) / radius;
            Vector3 refl = JobGlobal.emit(r.direction, n, mat.distance);
            or.bAlive    = 1;
            or.origin    = p;
            or.direction = refl;
            or.color     = r.color * mat.albedo;
            t            = temp;
            return(true);
        }

        temp = (-b + Mathf.Sqrt(b * b - a * c)) / a;
        if (temp < tMax && temp > tMin)
        {
            Vector3 p    = r.origin + temp * r.direction;
            Vector3 n    = (p - center) / radius;
            Vector3 refl = JobGlobal.emit(r.direction, n, mat.distance);
            or.bAlive    = 1;
            or.origin    = p;
            or.direction = refl;
            or.color     = r.color * mat.albedo;
            t            = temp;
            return(true);
        }

        return(false);
    }
    public void Execute(int index)
    {
        if (sourceRays[index].bAlive == 0)
        {
            destRays[index] = new jRay()
            {
                bAlive = 0,
                color  = sourceRays[index].color
            };
            return;
        }

        float closestSoFar = t_max;
        bool  hitAnything  = false;

        for (int i = 0; i < objs.Length; i++)
        {
            jRay            r        = sourceRays[index];
            jCommonMaterial material = objs[i].mat;
            float           t        = t_max;
            jRay            or       = new jRay();
            if (objs[i].HitSphere(r, t_min, closestSoFar, material, out or, out t))
            {
                hitAnything     = true;
                closestSoFar    = t;
                destRays[index] = or;
            }
        }

        if (!hitAnything)
        {
            destRays[index] = new jRay()
            {
                bAlive = 0,
                color  = sourceRays[index].color * JobGlobal.envColor
            };
        }
    }