static float3 Trace(Ray r, int depth, ref int inoutRayCount, ref SpheresSoA spheres, NativeArray <Material> materials, ref uint randState, bool doMaterialE = true) { Hit rec = default(Hit); int id = 0; ++inoutRayCount; if (HitWorld(r, kMinT, kMaxT, ref rec, ref id, ref spheres)) { Ray scattered; float3 attenuation; float3 lightE; var mat = materials[id]; var matE = mat.emissive; if (depth < kMaxDepth && Scatter(mat, r, rec, out attenuation, out scattered, out lightE, ref inoutRayCount, ref spheres, materials, ref randState)) { #if DO_LIGHT_SAMPLING if (!doMaterialE) { matE = new float3(0, 0, 0); } doMaterialE = (mat.type != Material.Type.Lambert); #endif return(matE + lightE + attenuation * Trace(scattered, depth + 1, ref inoutRayCount, ref spheres, materials, ref randState, doMaterialE)); } else { return(matE); } } else { // sky float3 unitDir = r.dir; float t = 0.5f * (unitDir.y + 1.0f); return(((1.0f - t) * new float3(1.0f, 1.0f, 1.0f) + t * new float3(0.5f, 0.7f, 1.0f)) * 0.3f); } }
static bool Scatter(Material mat, Ray r_in, Hit rec, out float3 attenuation, out Ray scattered, out float3 outLightE, ref int inoutRayCount, ref SpheresSoA spheres, NativeArray <Material> materials, ref uint randState) { outLightE = new float3(0, 0, 0); if (mat.type == Material.Type.Lambert) { // random point inside unit sphere that is tangent to the hit point float3 target = rec.pos + rec.normal + MathUtil.RandomUnitVector(ref randState); scattered = new Ray(rec.pos, normalize(target - rec.pos)); attenuation = mat.albedo; // sample lights #if DO_LIGHT_SAMPLING for (int j = 0; j < spheres.emissiveCount; ++j) { int i = spheres.emissives[j]; //@TODO if (&mat == &smat) // continue; // skip self //var s = spheres[i]; float3 scenter = new float3(spheres.centerX[i], spheres.centerY[i], spheres.centerZ[i]); float sqRradius = spheres.sqRadius[i]; // create a random direction towards sphere // coord system for sampling: sw, su, sv float3 sw = normalize(scenter - rec.pos); float3 su = normalize(cross(abs(sw.x) > 0.01f ? new float3(0, 1, 0) : new float3(1, 0, 0), sw)); float3 sv = cross(sw, su); // sample sphere by solid angle float cosAMax = sqrt(max(0.0f, 1.0f - sqRradius / lengthSquared(rec.pos - scenter))); float eps1 = RandomFloat01(ref randState), eps2 = RandomFloat01(ref randState); float cosA = 1.0f - eps1 + eps1 * cosAMax; float sinA = sqrt(1.0f - cosA * cosA); float phi = 2 * PI * eps2; float3 l = su * cos(phi) * sinA + sv * sin(phi) * sinA + sw * cosA; l = normalize(l); // shoot shadow ray Hit lightHit = default(Hit); int hitID = 0; ++inoutRayCount; if (HitWorld(new Ray(rec.pos, l), kMinT, kMaxT, ref lightHit, ref hitID, ref spheres) && hitID == i) { float omega = 2 * PI * (1 - cosAMax); float3 rdir = r_in.dir; float3 nl = dot(rec.normal, rdir) < 0 ? rec.normal : -rec.normal; outLightE += (mat.albedo * materials[i].emissive) * (max(0.0f, dot(l, nl)) * omega / PI); } } #endif return(true); } else if (mat.type == Material.Type.Metal) { float3 refl = reflect(r_in.dir, rec.normal); // reflected ray, and random inside of sphere based on roughness scattered = new Ray(rec.pos, normalize(refl + mat.roughness * RandomInUnitSphere(ref randState))); attenuation = mat.albedo; return(dot(scattered.dir, rec.normal) > 0); } else if (mat.type == Material.Type.Dielectric) { float3 outwardN; float3 rdir = r_in.dir; float3 refl = reflect(rdir, rec.normal); float nint; attenuation = new float3(1, 1, 1); float3 refr; float reflProb; float cosine; if (dot(rdir, rec.normal) > 0) { outwardN = -rec.normal; nint = mat.ri; cosine = mat.ri * dot(rdir, rec.normal); } else { outwardN = rec.normal; nint = 1.0f / mat.ri; cosine = -dot(rdir, rec.normal); } if (Refract(rdir, outwardN, nint, out refr)) { reflProb = Schlick(cosine, mat.ri); } else { reflProb = 1; } if (RandomFloat01(ref randState) < reflProb) { scattered = new Ray(rec.pos, normalize(refl)); } else { scattered = new Ray(rec.pos, normalize(refr)); } } else { attenuation = new float3(1, 0, 1); scattered = default(Ray); return(false); } return(true); }
static bool HitWorld(Ray r, float tMin, float tMax, ref Hit outHit, ref int outID, ref SpheresSoA spheres) { outID = spheres.HitSpheres(ref r, tMin, tMax, ref outHit); return(outID != -1); }
public Test() { s_SpheresSoA = new SpheresSoA(s_SpheresData.Length); }