ColorRGB RayTracer::trace(const Ray& p_ray, const SceneList& p_scene, int p_bounce) { if (p_bounce > m_settings.maxBounces) { return m_settings.clearColor; } std::pair<Shape*, float> closestInfo = getClosestShape(p_ray, p_scene); if (closestInfo.first != nullptr) { Point3 intersectionPoint = p_ray.origin + closestInfo.second * p_ray.direction; Vector3 intersectionNormal = closestInfo.first->getNormal(intersectionPoint); intersectionNormal.normalize(); // Offset to prevent self intersection intersectionPoint += 0.0001f * intersectionNormal; //ColorRGB reflectedColor = ColorRGB(255, 255, 255); if (closestInfo.first->reflective) { // Trace reflection ray Ray reflectionRay( intersectionPoint, reflect(p_ray.direction, intersectionNormal)); return trace(reflectionRay, p_scene, p_bounce + 1); } if (m_settings.lightEnabled == false) { return closestInfo.first->color.rgb(); } // Compute shadow ray const Point3 lightPosition(0, 500, 0); Vector3 shadowVector = lightPosition - intersectionPoint; shadowVector.normalize(); Ray shadowRay(intersectionPoint, shadowVector); std::pair<Shape*, float> shadowInfo = getClosestShape(shadowRay, p_scene); float lightContribution = 0.2f; // Ambient light if (shadowInfo.first == nullptr) { // Compute diffuse light factor float diffuseFactor = dotProduct(intersectionNormal, shadowRay.direction); if (diffuseFactor > 0.0f) { lightContribution += diffuseFactor; } } return std::min(lightContribution, 1.0f) * closestInfo.first->color.rgb(); } return m_settings.clearColor; }
RGBQUAD TraceOneRay(CRay ray, CSphere S[], CVector3D lightsArray[],int level, CBox sceneBox) { int n; RGBQUAD currentColor; currentColor.rgbBlue=20; currentColor.rgbGreen=20; currentColor.rgbRed=20; CVector3D hit,light,lightdir,snormal; if(checkIntersection(ray,S,&hit,&n,sceneBox)) { currentColor.rgbBlue=0; currentColor.rgbGreen=0; currentColor.rgbRed=0; //Вычисляем нормаль snormal.x=(hit.x-S[n].center.x); snormal.y=(hit.y-S[n].center.y); snormal.z=(hit.z-S[n].center.z); snormal.NormalizeVector(); //Проверяем каждый источник света for (int lightIndex=0; lightIndex<LIGHT_COUNT; lightIndex++) { light=lightsArray[lightIndex]; lightdir.x=light.x-hit.x; lightdir.y=light.y-hit.y; lightdir.z=light.z-hit.z; lightdir.NormalizeVector(); bool inShadow=false; //Находим коэффициент освещения float coefLight=snormal*lightdir; //Если коэффициент больше 0 то свет попадает if(coefLight>0){ //луч от источника света к месту пересечения с объектом CRay lightRay(hit,lightdir); //Проверяем не затенен ли данный объект другим объектом for (int index=0; index<SPHERE_COUNT; index++) { /*if(index==n) continue;*/ CVector3D lightHit; int num; if (checkIntersection(lightRay,S,&lightHit,&num,sceneBox)) { inShadow=true; break; } } if(!inShadow) { //Создаем отражающий луч CVector3D reflectionVector=(ray.vector-2*(((ray.vector*snormal))*snormal)); CRay reflectionRay(hit,reflectionVector); RGBQUAD reflectionColor; float coef=1; for (int i=0; i<level; i++) { //coef*=reflectionVector*ray.vector; coef*=0.5; } reflectionColor.rgbBlue=0; reflectionColor.rgbGreen=0; reflectionColor.rgbRed=0; if(level<7) { reflectionColor=TraceOneRay(reflectionRay,S,lightsArray,++level,sceneBox); } //Свет по Ламберту if (reflectionColor.rgbRed==20 && reflectionColor.rgbGreen==20 && reflectionColor.rgbBlue==20) { reflectionColor.rgbBlue=0; reflectionColor.rgbGreen=0; reflectionColor.rgbRed=0; } else { int k=1; } if ((int)currentColor.rgbRed+coefLight*S[n].color.rgbRed*0.5+reflectionColor.rgbRed*coef>=255) { currentColor.rgbRed=255; } else { currentColor.rgbRed+=coefLight*S[n].color.rgbRed*0.5+reflectionColor.rgbRed*coef; } if ((int)currentColor.rgbGreen+coefLight*S[n].color.rgbGreen*0.5+reflectionColor.rgbGreen*coef>=255) { currentColor.rgbGreen=255; } else { currentColor.rgbGreen+=coefLight*S[n].color.rgbGreen*0.5+reflectionColor.rgbGreen*coef; } if ((int)currentColor.rgbBlue+coefLight*S[n].color.rgbBlue*0.5+reflectionColor.rgbBlue*coef>=255) { currentColor.rgbBlue=255; } else { currentColor.rgbBlue+=coefLight*S[n].color.rgbBlue*0.5+reflectionColor.rgbBlue*coef; } //Блинн-Фонг взято с http://www.codermind.com/articles/Raytracer-in-C++-Part-II-Specularity-post-processing.html float fViewProjection=ray.vector*snormal; CVector3D blinnVector=lightRay.vector-ray.vector; float temp=blinnVector*blinnVector; if (temp!=0) { float blinn=1/sqrtSSE(temp) * max(coefLight-fViewProjection,0.0f); blinn=coef*powf(blinn,20); if ((int)currentColor.rgbRed+blinn*S[n].color.rgbRed>=255) { currentColor.rgbRed=255; } else { currentColor.rgbRed+=blinn*S[n].color.rgbRed; } if ((int)currentColor.rgbGreen+blinn*S[n].color.rgbGreen>=255) { currentColor.rgbGreen=255; } else { currentColor.rgbGreen+=blinn*S[n].color.rgbGreen; } if ((int)currentColor.rgbBlue+blinn*S[n].color.rgbBlue>=255) { currentColor.rgbBlue=255; } else { currentColor.rgbBlue+=blinn*S[n].color.rgbBlue; } } } } } } return currentColor; }
Eigen::Vector4d getColor(const Ray &ray, unsigned int recursionLevel, unsigned int transDepth, std::array<int, MAX_DEPTH + 1> &objStack) { BOOST_ASSERT_MSG(std::abs(1 - ray.dir.norm()) < EPSILON, "Got ray with non-unit direction"); const intersection_t isect = getIntersection(ray); int objId; if ((objId = isect.objId) < 0) return Eigen::Vector4d::Zero(); auto &objects = scene.getObjects(); auto &lights = scene.getLights(); Eigen::Vector4d I = Eigen::Vector4d::Zero(); Eigen::Vector4d pointOfIntersection = ray.origin + isect.dist * ray.dir; Eigen::Vector4d N = objects[objId]->getUnitNormal(pointOfIntersection); Eigen::Vector4d V = -1 * ray.dir; auto mat = scene.getMaterial(objects[objId]->matId); for (unsigned int i = 0 ; i < lights.size(); ++i) { if (lights[i]->isAmbient()) { I += mat.Ka * lights[i]->getAmountOfLight(pointOfIntersection).cwiseProduct(mat.rgb); continue; } Eigen::Vector4d L = lights[i]->getVectorToLight(pointOfIntersection); Ray rayToLight(pointOfIntersection, L, objId); bool lightVisible = true; if (lights[i]->getShadowOn()) { double distToBlockingObject = getIntersection(rayToLight).dist; double distToLight = (pointOfIntersection - lights[i]->getPosition()).norm(); lightVisible = distToBlockingObject <= EPSILON || distToBlockingObject >= distToLight; } if (lightVisible) { /* Diffuse Reflection */ Eigen::Vector4d Il = lights[i]->getAmountOfLight(pointOfIntersection); // Amount of light visible on surface determined by angle to light source double lambertCos = L.dot(N); if (lambertCos > 0) { I += mat.Kd * lambertCos * mat.rgb.cwiseProduct(Il); } else { continue; } /* Specular Reflection */ Eigen::Vector4d reflection = 2 * N.dot(rayToLight.dir) * N - rayToLight.dir; double specCoeff = reflection.dot(V); if (specCoeff > 0) { specCoeff = std::max(0.0, pow(specCoeff, mat.ns)); I += specCoeff * mat.Ks * mat.rgb.cwiseProduct(Il); } } } if (recursionLevel < MAX_DEPTH) { // Work out where in material stack we are int nextTransDepth; int nextObjId; if (objStack[transDepth] == objId) { nextTransDepth = transDepth - 1; nextObjId = objStack[transDepth - 1]; } else { nextTransDepth = transDepth + 1; objStack[nextTransDepth] = objId; nextObjId = objId; } // Compute intensity of reflected ray, if necessary if (reflect && mat.Kr > 0) { Ray reflectionRay(pointOfIntersection, ray.dir - (2 * N.dot(ray.dir)) * N, nextObjId); I += mat.Kr * getColor(reflectionRay, recursionLevel + 1, nextTransDepth, objStack); } // Compute intensity of transmission ray, if necessary if (refract && mat.Kt > 0) { const double etaIncident = (objStack[transDepth] == ID_AIR) ? 1.0 : scene.getMaterial(objects[objStack[transDepth]]->matId).Irefract; double cosThetaI, etaRefract; if (objStack[transDepth] == objId) { // Exiting a material cosThetaI = ray.dir.dot(N); etaRefract = (objStack[transDepth - 1] == ID_AIR) ? 1.0 : scene.getMaterial(objects[objStack[transDepth - 1]]->matId).Irefract; } else { cosThetaI = V.dot(N); etaRefract = scene.getMaterial(objects[objId]->matId).Irefract; } const double n = etaIncident/etaRefract; const double cosThetaR = sqrt(1 - (n * n) * (1 - cosThetaI * cosThetaI)); Ray T(pointOfIntersection, (n * ray.dir - (cosThetaR - n * cosThetaI) * N).normalized(), nextObjId); I += mat.Kt * getColor(T, recursionLevel + 1, nextTransDepth, objStack); } } return I; }