// ----------------------------------------------------------- // Engine::Raytrace // Naive ray tracing: Intersects the ray with every primitive // in the scene to determine the closest intersection // ----------------------------------------------------------- Primitive* Engine::Raytrace( Ray& a_Ray, Color& a_Acc, int a_Depth, float a_RIndex, float& a_Dist ) { if (a_Depth > TRACEDEPTH) return 0; // trace primary ray a_Dist = 1000000.0f; vector3 pi; Primitive* prim = 0; int result; // find the nearest intersection for ( int s = 0; s < m_Scene->GetNrPrimitives(); s++ ) { Primitive* pr = m_Scene->GetPrimitive( s ); int res; if (res = pr->Intersect( a_Ray, a_Dist )) { prim = pr; result = res; // 0 = miss, 1 = hit, -1 = hit from inside primitive } } // no hit, terminate ray if (!prim) return 0; // handle intersection if (prim->IsLight()) { // we hit a light, stop tracing a_Acc = Color( 1, 1, 1 ); } else { // determine color at point of intersection pi = a_Ray.GetOrigin() + a_Ray.GetDirection() * a_Dist; // trace lights for ( int l = 0; l < m_Scene->GetNrPrimitives(); l++ ) { Primitive* p = m_Scene->GetPrimitive( l ); if (p->IsLight()) { Primitive* light = p; // calculate diffuse shading vector3 L = ((Sphere*)light)->GetCentre() - pi; NORMALIZE( L ); vector3 N = prim->GetNormal( pi ); if (prim->GetMaterial()->GetDiffuse() > 0) { float dot = DOT( N, L ); if (dot > 0) { float diff = dot * prim->GetMaterial()->GetDiffuse(); // add diffuse component to ray color a_Acc += diff * prim->GetMaterial()->GetColor() * light->GetMaterial()->GetColor(); } } } } } // return pointer to primitive hit by primary ray return prim; }
ci::ColorA Scene::Raytrace(const ci::Ray& inRay, int inDepth, float& inDist, const float inRefractIndex) { if (inDepth > 3) return ci::ColorA::black(); Primitive* hitPrim = NULL; Primitive::E_INTERSECT_RESULT result; float currentInDist = inDist; for (int i=0; i<m_primitives.size(); i++) { Primitive* prim = m_primitives[i]; Primitive::E_INTERSECT_RESULT res = prim->Intersect(inRay, inDist); if (res != Primitive::MISS) { if (inDist <= currentInDist) { hitPrim = prim; result = res; } } } if (!hitPrim) return ci::ColorA::black(); if (hitPrim->IsLight()) return hitPrim->GetMaterial().GetDiffuseColor(); else { ci::Vec3f hitPoint = inRay.getOrigin() + inRay.getDirection() * inDist; ci::ColorA accumColor(0, 0, 0, 0); {//shading for (int i=0; i<m_primitives.size(); i++) { Primitive* prim = m_primitives[i]; if (prim->IsLight()) { Primitive* light = prim; //shadow feeler float shade = 1.0f; //get shadow feeler ray in direction of light ci::Vec3f L = (((SpherePrimitive*)light)->getCenter() - hitPoint).normalized(); float lightToHitPointDist = (((SpherePrimitive*)light)->getCenter() - hitPoint).length(); ci::Ray shadowFeelerRay(hitPoint + L * ci::EPSILON_VALUE, L); float LDist = 1000.0f; for (int j = 0; j<m_primitives.size(); j++) { Primitive* primJ = m_primitives[j]; if (primJ->IsLight()) break; else { Primitive::E_INTERSECT_RESULT result = primJ->Intersect(shadowFeelerRay, LDist); if (result == Primitive::HIT && LDist <= lightToHitPointDist) { shade = 0.0f; break; } } } ci::Vec3f N = hitPrim->GetNormal(hitPoint); float dotLN = L.dot(N); ci::Vec3f R = (L - 2.0f * dotLN * N).normalized(); ci::Vec3f V = inRay.getDirection().normalized(); if (dotLN > 0) { //calculate diffuse component float diffuseC = dotLN * hitPrim->GetMaterial().GetDiffuseCoefficient(); accumColor += diffuseC * hitPrim->GetMaterial().GetDiffuseColor() * light->GetMaterial().GetDiffuseColor() * shade; float dotVR = V.dot(R); if (dotVR > 0) { //calculate specular component float specularC = ci::math<float>::pow(dotVR, 20) * hitPrim->GetMaterial().GetSpecularCoefficient(); accumColor += specularC * hitPrim->GetMaterial().GetSpecularColor() * light->GetMaterial().GetDiffuseColor() * shade; } } } } } {//reflection if (hitPrim->GetMaterial().GetReflectionCoefficient() > 0.0f) { ci::Vec3f N = hitPrim->GetNormal(hitPoint); ci::Vec3f V = inRay.getDirection().normalized(); float dotVN = V.dot(N); ci::Vec3f R = (V - 2.0f * dotVN * N).normalized(); float reflectDist = 10000.0f; ci::ColorA reflectedColor = Raytrace(ci::Ray(hitPoint + R * ci::EPSILON_VALUE * 2, R), inDepth + 1, reflectDist, inRefractIndex); accumColor += reflectedColor * hitPrim->GetMaterial().GetDiffuseColor() * hitPrim->GetMaterial().GetReflectionCoefficient(); } } {//refraction if (hitPrim->GetMaterial().GetRefractionCoefficient() > 0.0f) { float refractIndex = hitPrim->GetMaterial().GetRefractionCoefficient(); float n = inRefractIndex / refractIndex; ci::Vec3f N = hitPrim->GetNormal(hitPoint) * (int)result; float cosI = N.dot(inRay.getDirection()); //float I = acos(cosI); float cosT2 = 1.0f - n * n * (1.0f - cosI * cosI); //float sinT2 = n * n * (1.0f - cosI * cosI); if (cosT2 >= 0.0f) { //if (sinT2 <= 1.0f) { //Vec3f T = ((n * inRay.getDirection()) - (n + sqrtf(1 - sinT2)) * N).normalized(); float cosT = sqrtf(cosT2); Vec3f T = ((n * inRay.getDirection()) - (n * cosI + sqrtf(cosT2)) * N).normalized(); float refractDist = 10000.0f; ci::ColorA refractedColor = Raytrace(ci::Ray(hitPoint + T * ci::EPSILON_VALUE * 1, T), inDepth + 1, refractDist, refractIndex); accumColor += refractedColor * hitPrim->GetMaterial().GetDiffuseColor() * hitPrim->GetMaterial().GetRefractionCoefficient(); } } } {//diffuse reflection - for color bleeding, ambient occlusion } {//soft shadows } {//anti-aliasing my multi-sampling } return accumColor; } }