private bool RaySphereCollision(TDRay ray, float hitDistanceMin, float hitDistanceMax, ref TDRayHitRecord hitRecord) { bool hasHit = false; hitRecord.distance = float.MaxValue; foreach (TDSphere sphere in this.scene.spheres) { float3 oc = ray.origin - sphere.position; float a = math.dot(ray.direction, ray.direction); float b = 2.0f * math.dot(oc, ray.direction); float c = math.dot(oc, oc) - sphere.radius * sphere.radius; float discriminant = b * b - 4.0f * a * c; if (discriminant > 0) { float hitDistance = (-b - math.sqrt(discriminant)) / (2.0f * a); if (hitDistance > hitDistanceMin && hitDistance < hitDistanceMax) { if (!hasHit || hitDistance < hitRecord.distance) { float3 hitPoint = ray.origin + hitDistance * ray.direction; float3 hitNormal = math.normalize(hitPoint - sphere.position); hitRecord.point = hitPoint; hitRecord.normal = hitNormal; hitRecord.material = sphere.material; hitRecord.albedo = sphere.albedo; hitRecord.emission = sphere.emission; hitRecord.distance = hitDistance; hitRecord.fuzz = sphere.fuzz; hitRecord.refractiveIndex = sphere.refractiveIndex; hasHit = true; continue; } } hitDistance = (-b + math.sqrt(discriminant)) / (2.0f * a); if (hitDistance > hitDistanceMin && hitDistance < hitDistanceMax) { if (!hasHit || hitDistance < hitRecord.distance) { float3 hitPoint = ray.origin + hitDistance * ray.direction; float3 hitNormal = math.normalize(hitPoint - sphere.position); hitRecord.point = hitPoint; hitRecord.normal = hitNormal; hitRecord.material = sphere.material; hitRecord.albedo = sphere.albedo; hitRecord.emission = sphere.emission; hitRecord.distance = hitDistance; hitRecord.fuzz = sphere.fuzz; hitRecord.refractiveIndex = sphere.refractiveIndex; hasHit = true; } } } } return(hasHit); }
private float3 TraceColor(TDRay ray, Unity.Mathematics.Random random) { TDRayHitRecord hitRecord = new TDRayHitRecord(); float3[] emissions = new float3[this.renderConfiguration.maxBounces]; float3[] attenuations = new float3[this.renderConfiguration.maxBounces]; float3 result = this.ambientLight; int depth = this.renderConfiguration.maxBounces - 1; for (int i = 0; i < this.renderConfiguration.maxBounces; ++i) { attenuations[i] = this.ambientLight; if (RaySphereCollision(ray, 0.0001f, float.MaxValue, ref hitRecord)) { float3 attenuation = float3.zero; float3 scatteredRayDirection = float3.zero; bool scatter = false; if (hitRecord.material == 1.0f) { //Lambertian float3 target = hitRecord.point + hitRecord.normal + this.RandomPointInUnitSphere(random); scatteredRayDirection = target - hitRecord.point; attenuation = hitRecord.albedo; scatter = true; } else if (hitRecord.material == 2.0f) { //Metal scatteredRayDirection = ray.direction - 2.0f * math.dot(ray.direction, hitRecord.normal) * hitRecord.normal + hitRecord.fuzz * this.RandomPointInUnitSphere(random) * hitRecord.fuzz; attenuation = hitRecord.albedo; if (math.dot(scatteredRayDirection, hitRecord.normal) > 0.0f) { scatter = true; } } else if (hitRecord.material == 3.0f) { //Refractive float3 reflectedDirection = ray.direction - 2.0f * math.dot(ray.direction, hitRecord.normal) * hitRecord.normal; float3 outwardNormal = float3.zero; float3 refractedDirection = float3.zero; float reflectProbablity = 1.0f; float cosine = 0.0f; float niOverNt = 0.0f; attenuation = new float3(1.0f, 1.0f, 1.0f); if (math.dot(ray.direction, hitRecord.normal) > 0.0f) { outwardNormal = -hitRecord.normal; niOverNt = hitRecord.refractiveIndex; cosine = hitRecord.refractiveIndex * math.dot(ray.direction, hitRecord.normal) / math.length(ray.direction); } else { outwardNormal = hitRecord.normal; niOverNt = 1.0f / hitRecord.refractiveIndex; cosine = -math.dot(ray.direction, hitRecord.normal) / math.length(ray.direction); } float dt = math.dot(ray.direction, hitRecord.normal); float discriminant = 1.0f - niOverNt * niOverNt * (1.0f - dt * dt); if (discriminant > 0.0f) { //Refract refractedDirection = niOverNt * (ray.direction - outwardNormal * dt) - outwardNormal * math.sqrt(discriminant); //Schlick Approximation float r0 = (1.0f - hitRecord.refractiveIndex) / (1.0f + hitRecord.refractiveIndex); r0 = r0 * r0; reflectProbablity = r0 + (1 - r0) * math.pow((1.0f - cosine), 5.0f); } if (random.NextFloat() < reflectProbablity) { scatteredRayDirection = reflectedDirection; } else { scatteredRayDirection = refractedDirection; } scatter = true; } if (scatter) { //TDRay scatteredRay = new TDRay(hitRecord.point, scatteredRayDirection, ray.uv); ray.origin = hitRecord.point; ray.direction = math.normalize(scatteredRayDirection); emissions[i] = hitRecord.emission; attenuations[i] = attenuation; //return hitRecord.emission + attenuation * TraceColor(scatteredRay, numBounces + 1); } else { depth = i; break; } //return float3.zero; } else { depth = i; break; } //return this.ambientLight; } for (int i = depth; i >= 0; --i) { result = emissions[i] + result * attenuations[i]; } return(result); }