glm::vec3 indirect(const glm::vec3 &c, const glm::vec3 &p, const glm::vec3 &n, const Glass &glass, int iterations) { float ior = 1.5f; // Calcul du rayon réfléchi glm::vec3 direction = reflect(c,n); glm::vec3 directionNorm = glm::normalize(direction); Ray rayReflexion{p+directionNorm*0.1f, directionNorm}; // Calcul du rayon réfracté glm::vec3 vecRefract; refract(-c, n, ior, vecRefract); Ray rayRefraction{p+vecRefract*0.1f, vecRefract}; // Calcul du coefficient reflechi/refracte float coeff = fresnelR(-c, n, ior); float rand = random_u(); if (rand < coeff) { return radiance(rayReflexion, iterations-1); } return radiance(rayRefraction, iterations-1) * glass.color; //return radiance(rayReflexion, iterations-1) * coeff + radiance(rayRefraction, iterations-1) * (1- coeff); }
glm::vec3 indirect(const glm::vec3 &c, const glm::vec3 &p, const glm::vec3 &n, const Diffuse &diffuse, int iterations) { float pdf; glm::vec3 directionRay = sample_sphere(1, random_u(), random_u(), pdf, n); Ray ray{p+directionRay*0.1f, directionRay}; return radiance(ray, iterations-1) * diffuse.color; }
static cv::Mat generateRadianceMap(const cv::Mat& rc) { cv::Size size = getImage(0).size(); cv::Mat_<cv::Vec3f> radiance(size); for (int y = 0; y < size.height; ++y) { for (int x = 0; x < size.width; ++x) { cv::Vec3f sum(0,0,0), wsum(0,0,0); for (int p = 0, n = numImages(); p < n; ++p) { const cv::Mat& img = getImage(p); cv::Vec3b c = img.at<cv::Vec3b>(y, x); float exptime = getExposureTime(p); cv::Vec3f tmp, wtmp(weight(c[0]), weight(c[1]), weight(c[2])); tmp[0] = (rc.at<float>(c[0], 0) - std::log(exptime)) * wtmp[0]; tmp[1] = (rc.at<float>(c[1], 1) - std::log(exptime)) * wtmp[1]; tmp[2] = (rc.at<float>(c[2], 2) - std::log(exptime)) * wtmp[2]; sum += tmp; wsum += wtmp; } cv::Vec3f tmp; tmp[0] = std::exp( sum[0] / wsum[0] ); tmp[1] = std::exp( sum[1] / wsum[1] ); tmp[2] = std::exp( sum[2] / wsum[2] ); radiance.at<cv::Vec3f>(y, x) = tmp; } } return radiance; }
glm::vec3 indirect(const glm::vec3 &c, const glm::vec3 &p, const glm::vec3 &n, const Mirror &mirror, int iterations) { glm::vec3 direction = reflect(c,n); glm::vec3 directionNorm = glm::normalize(direction); Ray r{p+directionNorm*0.1f, directionNorm}; return radiance(r, iterations-1) * mirror.color; }
int main(int argc, char *argv[]){ int w=1024, h=768, samps = argc==2 ? atoi(argv[1])/4 : 1; // # samples Ray cam(Vec(50,52,295.6), Vec(0,-0.042612,-1).norm()); // cam pos, dir Vec cx=Vec(w*.5135/h), cy=(cx%cam.d).norm()*.5135, r, *c=new Vec[w*h]; #pragma omp parallel for schedule(dynamic, 1) private(r) // OpenMP for (int y=0; y<h; y++){ // Loop over image rows // *** Commented out for Visual Studio, fprintf is not thread-safe //fprintf(stderr,"\rRendering (%d spp) %5.2f%%",samps*4,100.*y/(h-1)); unsigned short Xi[3]={0,0,y*y*y}; // *** Moved outside for VS2012 for (unsigned short x=0; x<w; x++) // Loop cols for (int sy=0, i=(h-y-1)*w+x; sy<2; sy++) // 2x2 subpixel rows for (int sx=0; sx<2; sx++, r=Vec()){ // 2x2 subpixel cols for (int s=0; s<samps; s++){ double r1=2*erand48(Xi), dx=r1<1 ? sqrt(r1)-1: 1-sqrt(2-r1); double r2=2*erand48(Xi), dy=r2<1 ? sqrt(r2)-1: 1-sqrt(2-r2); Vec d = cx*( ( (sx+.5 + dx)/2 + x)/w - .5) + cy*( ( (sy+.5 + dy)/2 + y)/h - .5) + cam.d; r = r + radiance(Ray(cam.o+d*140,d.norm()),0,Xi)*(1./samps); } // Camera rays are pushed ^^^^^ forward to start in interior c[i] = c[i] + Vec(clamp(r.x),clamp(r.y),clamp(r.z))*.25; } } FILE *f = fopen("image.ppm", "w"); // Write image to PPM file. fprintf(f, "P3\n%d %d\n%d\n", w, h, 255); for (int i=0; i<w*h; i++) fprintf(f,"%d %d %d ", toInt(c[i].x), toInt(c[i].y), toInt(c[i].z)); }
//------------------------------------------------------------------------------- // @ Player::Player() //------------------------------------------------------------------------------- // Constructor //------------------------------------------------------------------------------- Player::Player() { mRadius = 5.0f; mSphereIndices = nullptr; mSphereVerts = nullptr; mShader = IvRenderer::mRenderer->GetResourceManager()->CreateShaderProgram( IvRenderer::mRenderer->GetResourceManager()->CreateVertexShaderFromFile( "directionalShader"), IvRenderer::mRenderer->GetResourceManager()->CreateFragmentShaderFromFile( "directionalShader")); IvRenderer::mRenderer->SetShaderProgram(mShader); IvVector3 radiance(1.0f, 1.0f, 1.0f); mShader->GetUniform("dirLightRadiance")->SetValue(radiance, 0); mRotate.Identity(); mLightDir = IvVector3(1.0f, 0.0f, 0.0f); CreateSphere(); } // End of Player::Player()
glm::vec3 indirect(const Ray &rOrigine, const Ray &rReflect, const glm::vec3 &p, const glm::vec3 & n, int countdown, const Diffuse &diffuse) { float pdf; glm::vec3 w = glm::normalize(sample_sphere(1, random_u(), random_u(), pdf, n)); Ray rIndirect{p+0.1f*w, w}; return radiance(rIndirect, countdown); //return glm::vec3(0,0,0); }
void Renderer::render() const { Ray ray; P2d pp; Random rnd(3141592); C3f *image = new C3f[ _vp.width * _vp.height]; double invNumSample = 1.0 / _vp.numSamples; // OpenMP #pragma omp parallel for schedule(dynamic, 1) num_threads(4) for (int y = 0; y < _vp.height; y++) { std::cerr << "Rendering (y = " << y << ") " << (100.0 * y / (_vp.height - 1)) << "%" << std::endl; for (int x = 0; x < _vp.width; x++) { const int imageIndex = (_vp.height - y - 1) * _vp.width + x; C3f pixelColor(0.0f, 0.0f, 0.0f); for (int j = 0; j < _vp.numSamples; j++) { pp.x = _vp.s*(- x + 0.5 * _vp.width); pp.y = _vp.s*(y - 0.5 * _vp.height); ray = _camera->getRay(pp); pixelColor = pixelColor + radiance(ray, &rnd, 0) / _vp.numSamples; } image[imageIndex] = C3f(pixelColor); } } savePPM("output.ppm", image, _vp.width, _vp.height); delete[] image; }
Vec3 DiffuseMaterial::out_direction(Vec3 const &in, Vec3 const &norm, double &brdf, RGB &color, CRandomMersenne *rng) { // Generamos una nueva direccion. Sera una direccion aleatoria en la // semiesfera definida por la normal. brdf = 1.0f; color = radiance() * brdf; return versor(random_dir(norm, rng->Random(), rng->Random())); }
glm::vec3 indirect(const Ray &rOrigine, const Ray &rReflect, const glm::vec3 &p, const glm::vec3 & n, int countdown, const Glass &glass) { float fresnel = fresnelR(-rOrigine.direction,n, 1.5); glm::vec3 refracted; bool canRefract = refract(-rOrigine.direction, n, 1.5, refracted); Ray rRefracted{p+refracted*0.1f, refracted}; if(canRefract) { float u = random_u(); if(u < fresnel) return radiance(rReflect, countdown); else return radiance(rRefracted, countdown); } //return fresnel*radiance(rReflect, countdown)+(1-fresnel)*radiance(rRefracted, countdown); else return fresnel*radiance(rReflect, countdown); }
Vec radiance(const Ray &r, int depth, unsigned short *Xi) { double t; // distance to intersection int id = 0; // id of intersected object if(!intersect(r, t, id)) return Vec(); // if miss, return black const Sphere &obj = spheres[id]; // the hit object Vec x = r.o + r.d*t, n = (x - obj.p).norm(), nl = n.dot(r.d) < 0 ? n : n*-1, f = obj.c; double p = f.x > f.y && f.x>f.z ? f.x : f.y > f.z ? f.y : f.z; // max refl if(depth > 255) return obj.e; if(++depth > 5) if(erand48(Xi) < p) f = f*(1 / p); else return obj.e; //R.R. if(obj.refl == DIFF) { // Ideal DIFFUSE reflection double r1 = 2 * M_PI*erand48(Xi), r2 = erand48(Xi), r2s = sqrt(r2); Vec w = nl, u = ((fabs(w.x) > .1 ? Vec(0, 1) : Vec(1)) % w).norm(), v = w%u; Vec d = (u*cos(r1)*r2s + v*sin(r1)*r2s + w*sqrt(1 - r2)).norm(); return obj.e + f.mult(radiance(Ray(x, d), depth, Xi)); } else if(obj.refl == SPEC) // Ideal SPECULAR reflection return obj.e + f.mult(radiance(Ray(x, r.d - n * 2 * n.dot(r.d)), depth, Xi)); Ray reflRay(x, r.d - n * 2 * n.dot(r.d)); // Ideal dielectric REFRACTION bool into = n.dot(nl) > 0; // Ray from outside going in? double nc = 1, nt = 1.5, nnt = into ? nc / nt : nt / nc, ddn = r.d.dot(nl), cos2t; if((cos2t = 1 - nnt*nnt*(1 - ddn*ddn)) < 0) // Total internal reflection return obj.e + f.mult(radiance(reflRay, depth, Xi)); Vec tdir = (r.d*nnt - n*((into ? 1 : -1)*(ddn*nnt + sqrt(cos2t)))).norm(); double a = nt - nc, b = nt + nc, R0 = a*a / (b*b), c = 1 - (into ? -ddn : tdir.dot(n)); double Re = R0 + (1 - R0)*c*c*c*c*c, Tr = 1 - Re, P = .25 + .5*Re, RP = Re / P, TP = Tr / (1 - P); return obj.e + f.mult(depth > 2 ? (erand48(Xi) < P ? // Russian roulette radiance(reflRay, depth, Xi)*RP : radiance(Ray(x, tdir), depth, Xi)*TP) : radiance(reflRay, depth, Xi)*Re + radiance(Ray(x, tdir), depth, Xi)*Tr); }
void tracer::Pathtracer::trace(const Ray& ray) { // random number generation std::random_device rd; std::mt19937 engine(rd()); const bool debug = true; const bool considerEmission = true; glm::vec3 rad = radiance(ray, engine, 0, considerEmission, debug); std::cout << "returned radiance: " << rad << std::endl; }
int main(int argc, char **argv) { int width = 640; int height = 480; int photon_num = 50000; double gather_photon_radius = 32.0; int gahter_max_photon_num = 64; int final_gather = 64; // final_gather個のサンプル int direct_light_samples = 64; // カメラ位置 Ray camera(Vec(50.0, 52.0, 295.6), Normalize(Vec(0.0, -0.042612, -1.0))); // シーン内でのスクリーンのx,y方向のベクトル Vec cx = Vec(width * 0.5135 / height); Vec cy = Normalize(Cross(cx, camera.dir)) * 0.5135; Color *image = new Color[width * height]; // フォトンマップ構築 PhotonMap photon_map; create_photon_map(photon_num, &photon_map); // イラディアンスキャッシュ // 事前構築(なにかと都合がよい) IrradianceCache cache; for (int i = 0; i < 10000; i ++) { if (i % 1000 == 0) std::cout << "*"; pre_build_irradiance_cache(camera, cx, cy, width, height, &photon_map, &cache, gather_photon_radius, gahter_max_photon_num, final_gather, direct_light_samples); } for (int y = 0; y < height; y ++) { std::cerr << "Rendering " << (100.0 * y / (height - 1)) << "%" << std::endl; srand(y * y * y); for (int x = 0; x < width; x ++) { int image_index = y * width + x; image[image_index] = Color(); // 2x2のサブピクセルサンプリング for (int sy = 0; sy < 2; sy ++) { for (int sx = 0; sx < 2; sx ++) { // テントフィルターによってサンプリング // ピクセル範囲で一様にサンプリングするのではなく、ピクセル中央付近にサンプルがたくさん集まるように偏りを生じさせる const double r1 = 2.0 * rand01(), dx = r1 < 1.0 ? sqrt(r1) - 1.0 : 1.0 - sqrt(2.0 - r1); const double r2 = 2.0 * rand01(), dy = r2 < 1.0 ? sqrt(r2) - 1.0 : 1.0 - sqrt(2.0 - r2); Vec dir = cx * (((sx + 0.5 + dx) / 2.0 + x) / width - 0.5) + cy * (((sy + 0.5 + dy) / 2.0 + y) / height- 0.5) + camera.dir; image[image_index] = image[image_index] + radiance(Ray(camera.org + dir * 130.0, Normalize(dir)), 0, &photon_map, &cache, gather_photon_radius, gahter_max_photon_num, final_gather, direct_light_samples); } } } } // .hdrフォーマットで出力 save_hdr_file(std::string("image.hdr"), image, width, height); }
int render(const Setting& setting, Camera& camera, Screen& screen, Scene& scene, ImageBuffer& buffer) { int time_count = 1; clock_t start_time, current_time; float now_time; start_time = clock(); char fname[255]; for (int y = 0; y < setting.reso_h; y++) { Imath::Rand48 rnd; std::cout << "Rendering (y = " << y << ") " << (100.0 * y / (setting.reso_h - 1)) << "%" << std::endl; for (int x = 0; x < setting.reso_w; x++) { int index = (setting.reso_h - y - 1) * setting.reso_w + x; buffer[index] = Imath::C3f(0,0,0); for (int sy = 0; sy < setting.supersamples; sy++) { for (int sx = 0; sx < setting.supersamples; sx++) { Imath::C3f acm_rad = Imath::C3f(0,0,0); for (int s = 0; s < setting.samples; s++) { Ray ray = Ray(camera,screen,setting,x,y,sx,sy); acm_rad += radiance(scene, ray, rnd, 0) / setting.samples / (setting.supersamples * setting.supersamples); buffer[index] += acm_rad; current_time = clock(); now_time = static_cast<float>((current_time - start_time) / CLOCKS_PER_SEC); if (now_time > time_count * 60.0f) { std::cout << "width:" << setting.reso_w << std::endl; std::cout << "height:" << setting.reso_h << std::endl; std::cout << "sample:" << setting.samples << std::endl; std::cout << "subpixel:" << setting.supersamples << std::endl; std::cout << time_count << "minute(s)" << std::endl; std::cout << "image output..." << std::endl; sprintf(fname, "out_%02d.bmp", time_count); write_bmp(fname, buffer, setting); time_count++; } } } } } } return 0; }
inline void compute_inner(int y, int w, int h, int samps, Ray &cam, Vec &cx, Vec &cy, Vec &r, Vec *c) { //fprintf(stderr, "\rRendering (%d spp) %5.2f%%", samps * 4, 100.*y / (h - 1)); for(unsigned short x = 0, Xi[3] = { 0, 0, (unsigned short)(y*y*y) }; x < w; x++) // Loop cols for(int sy = 0, i = (h - y - 1)*w + x; sy < 2; sy++) // 2x2 subpixel rows for(int sx = 0; sx < 2; sx++, r = Vec()) { // 2x2 subpixel cols for(int s = 0; s < samps; s++) { double r1 = 2 * erand48(Xi), dx = r1 < 1 ? sqrt(r1) - 1 : 1 - sqrt(2 - r1); double r2 = 2 * erand48(Xi), dy = r2 < 1 ? sqrt(r2) - 1 : 1 - sqrt(2 - r2); Vec d = cx*(((sx + .5 + dx) / 2 + x) / w - .5) + cy*(((sy + .5 + dy) / 2 + y) / h - .5) + cam.d; r = r + radiance(Ray(cam.o + d * 140, d.norm()), 0, Xi)*(1. / samps); } // Camera rays are pushed ^^^^^ forward to start in interior c[i] = c[i] + Vec(clamp(r.x), clamp(r.y), clamp(r.z))*.25; } }
void pre_build_irradiance_cache(const Ray &camera, const Vec &cx, const Vec &cy, const int width, const int height, PhotonMap *photon_map, IrradianceCache *cache, const double gather_radius, const int gahter_max_photon_num, const int final_gather, const int direct_light_samples) { int x = rand01() * width; if (x >= width) x = width - 1; int y = rand01() * height; if (y >= height) y = height - 1; int sx = rand01() * 2; if (sx >= 2) sx = 1; int sy = rand01() * 2; if (sy >= 2) sy = 1; // テントフィルターによってサンプリング // ピクセル範囲で一様にサンプリングするのではなく、ピクセル中央付近にサンプルがたくさん集まるように偏りを生じさせる const double r1 = 2.0 * rand01(), dx = r1 < 1.0 ? sqrt(r1) - 1.0 : 1.0 - sqrt(2.0 - r1); const double r2 = 2.0 * rand01(), dy = r2 < 1.0 ? sqrt(r2) - 1.0 : 1.0 - sqrt(2.0 - r2); Vec dir = cx * (((sx + 0.5 + dx) / 2.0 + x) / width - 0.5) + cy * (((sy + 0.5 + dy) / 2.0 + y) / height- 0.5) + camera.dir; radiance(Ray(camera.org + dir * 130.0, Normalize(dir)), 0, photon_map, cache, gather_radius, gahter_max_photon_num, final_gather, direct_light_samples, true); }
C3f Renderer::radiance(const Ray & ray, Random *rnd, const int depth) const { Intersection intersection; if (!_scene.intersect(ray, &intersection)) { return C3f(0.0f, 0.0f, 0.0f); } double prob = intersection.pMaterial->getMaxColor(); if (depth > 5) { return C3f(0.0f, 0.0f, 0.0f); } else { prob = 1.0; } C3f incomingRadiance(0.0f, 0.0f, 0.0f); C3f weight(1.0f, 1.0f, 1.0f); V3d w, u, v; w = intersection.normal.dot(ray.dir) < 0.0 ? intersection.normal : (-1.0 * intersection.normal); if (fabs(w.x) > SR_EPSILON) { u = (V3d(0.0, 1.0, 0.0).cross(w)).normalized(); } else { u = (V3d(1.0, 0.0, 0.0).cross(w)).normalized(); } v = w.cross(u); const double r1 = SR_PITIMES2 * rnd->next01(); const double r2 = rnd->next01(), r2s = sqrt(r2); Ray next; next.pos = intersection.position; next.dir = ( u * cos(r1) * r2s + v * sin(r1) * r2s + w * sqrt(1.0 - r2)).normalized(); incomingRadiance = radiance(next, rnd, depth + 1); weight = intersection.pMaterial->shade() /prob; return intersection.pMaterial->getEmission() + weight*incomingRadiance; }
Vec radiance(const Ray &r, int depth, unsigned short *Xi,int E=1){ double t; // distance to intersection int id=0; // id of intersected object if (!intersect(r, t, id)) return Vec(); // if miss, return black const Sphere &obj = spheres[id]; // the hit object Vec x=r.o+r.d*t, n=(x-obj.p).norm(), nl=n.dot(r.d)<0?n:n*-1, f=obj.c; double p = f.x>f.y && f.x>f.z ? f.x : f.y>f.z ? f.y : f.z; // max refl if (++depth>5||!p) if (erand48(Xi)<p) f=f*(1/p); else return obj.e*E; if (obj.refl == DIFF){ // Ideal DIFFUSE reflection double r1=2*M_PI*erand48(Xi), r2=erand48(Xi), r2s=sqrt(r2); Vec w=nl, u=((fabs(w.x)>.1?Vec(0,1):Vec(1))%w).norm(), v=w%u; Vec d = (u*cos(r1)*r2s + v*sin(r1)*r2s + w*sqrt(1-r2)).norm(); // Loop over any lights Vec e; for (int i=0; i<numSpheres; i++){ const Sphere &s = spheres[i]; if (s.e.x<=0 && s.e.y<=0 && s.e.z<=0) continue; // skip non-lights Vec sw=s.p-x, su=((fabs(sw.x)>.1?Vec(0,1):Vec(1))%sw).norm(), sv=sw%su; double cos_a_max = sqrt(1-s.rad*s.rad/(x-s.p).dot(x-s.p)); double eps1 = erand48(Xi), eps2 = erand48(Xi); double cos_a = 1-eps1+eps1*cos_a_max; double sin_a = sqrt(1-cos_a*cos_a); double phi = 2*M_PI*eps2; Vec l = su*cos(phi)*sin_a + sv*sin(phi)*sin_a + sw*cos_a; l.norm(); if (intersect(Ray(x,l), t, id) && id==i){ // shadow ray double omega = 2*M_PI*(1-cos_a_max); e = e + f.mult(s.e*l.dot(nl)*omega)*M_1_PI; // 1/pi for brdf } } return obj.e*E+e+f.mult(radiance(Ray(x,d),depth,Xi,0)); } else if (obj.refl == SPEC) // Ideal SPECULAR reflection return obj.e + f.mult(radiance(Ray(x,r.d-n*2*n.dot(r.d)),depth,Xi)); Ray reflRay(x, r.d-n*2*n.dot(r.d)); // Ideal dielectric REFRACTION bool into = n.dot(nl)>0; // Ray from outside going in? double nc=1, nt=1.5, nnt=into?nc/nt:nt/nc, ddn=r.d.dot(nl), cos2t; if ((cos2t=1-nnt*nnt*(1-ddn*ddn))<0) // Total internal reflection return obj.e + f.mult(radiance(reflRay,depth,Xi)); Vec tdir = (r.d*nnt - n*((into?1:-1)*(ddn*nnt+sqrt(cos2t)))).norm(); double a=nt-nc, b=nt+nc, R0=a*a/(b*b), c = 1-(into?-ddn:tdir.dot(n)); double Re=R0+(1-R0)*c*c*c*c*c,Tr=1-Re,P=.25+.5*Re,RP=Re/P,TP=Tr/(1-P); return obj.e + f.mult(depth>2 ? (erand48(Xi)<P ? // Russian roulette radiance(reflRay,depth,Xi)*RP:radiance(Ray(x,tdir),depth,Xi)*TP) : radiance(reflRay,depth,Xi)*Re+radiance(Ray(x,tdir),depth,Xi)*Tr); }
Texture::Visualization::Visualization(const Any& a) { *this = Visualization(); if (a.type() == Any::ARRAY) { if (a.nameEquals("bumpInAlpha")) { *this = bumpInAlpha(); } else if (a.nameEquals("defaults")) { *this = defaults(); } else if (a.nameEquals("linearRGB")) { *this = linearRGB(); } else if (a.nameEquals("depthBuffer")) { *this = depthBuffer(); } else if (a.nameEquals("packedUnitVector")) { *this = packedUnitVector(); } else if (a.nameEquals("radiance")) { *this = radiance(); } else if (a.nameEquals("reflectivity")) { *this = reflectivity(); } else if (a.nameEquals("sRGB")) { *this = sRGB(); } else if (a.nameEquals("unitVector")) { *this = unitVector(); } else { a.verify(false, "Unrecognized Visualization factory method"); } } else { a.verifyName("Texture::Visualization", "Visualization"); AnyTableReader r(a); String c; if (r.getIfPresent("channels", c)) { channels = toChannels(c); } r.getIfPresent("documentGamma", documentGamma); r.getIfPresent("invertIntensity", invertIntensity); r.getIfPresent("max", max); r.getIfPresent("min", min); r.getIfPresent("layer", layer); r.getIfPresent("mipLevel", mipLevel); r.verifyDone(); } }
nex::color path_tracer::indirect_illumination(const sampler* sampler, const nex::ray* ray, const surface& surface) const { nex::vector wi; float pdf = 0.0f; nex::color f = surface.bsdf->sample(sampler->get_samples(bsdf_index)[ray->depth], surface, -ray->direction, &wi, &pdf); if ((f == nex::color::black()) || (pdf <= 0.0f)) { return nex::color::black(); } nex::ray reflected_ray(surface.position + nex::EPSILON * wi, wi, FLT_MAX, ray->depth + 1); bool emittance = (surface.bsdf->get_bxdf(BXDF_SPECULAR) != nullptr); float cos = std::abs(nex::dot(surface.normal, wi)); return radiance(sampler, &reflected_ray, emittance) * f * cos / pdf; }
void tracer::Pathtracer::render(unsigned width, unsigned height) { // initialize output texture m_output.clear(); m_output.resize(width * height); std::fill(m_output.begin(), m_output.end(), glm::vec4(0.0f)); // pinhole camera Camera& camera = m_scene.getCamera(); camera.setImagingPlaneResolution(width, height); // random number generation std::random_device rd; std::mt19937 engine(rd()); // progress printing std::cout.precision(2); std::cout.setf(std::ios::fixed, std::ios::floatfield); std::string status = "Rendering, done: "; float percentage = 0.0f, fraction = 100.0f/height; // rendering loop #pragma omp parallel for schedule(dynamic) for (unsigned y = 0; y < height; y++) { for (unsigned x = 0; x < width; x++) { glm::vec3 outputColor(0.0f); for (unsigned s = 0; s < m_samples; s++) { Ray ray = camera.castRay(x, y); outputColor += radiance(ray, engine) / (float)m_samples; } m_output[y*width+x] = glm::vec4(color::gammaCompress(glm::clamp(outputColor, 0.0f, 1.0f)), 1.0f); } #pragma omp critical { percentage += fraction; std::cout << "\r" << status << percentage << "%"; } } std::cout << std::endl << std::endl; }
int main(int argc, char *argv[]) { // Sphere* S = new Sphere(16.5, Vec(0,0,0), Vec(), Vec(1,1,1)*.999, SPEC); // S->intersect(Ray(Vec(50,50,100),Vec(0,0,-1).norm())); //return 0; // int w=1024, h=768, samps = argc==2 ? atoi(argv[1])/4 : 1; // # samples // int w=512, h=384, samps = argc==2 ? atoi(argv[1])/4 : 1; // # samples int w=256, h=192, samps = argc==2 ? atoi(argv[1])/4 : 1; // # samples Ray cam(Vec(50,52,295.6), Vec(0,-0.042612,-1).norm()); // cam pos, dir Vec cx=Vec(w*.5135/h), cy=(cx%cam.d).norm()*.5135, r; Vec *frame=new Vec[w*h]; #pragma omp parallel for schedule(dynamic, 1) private(r) for (unsigned short y=0; y<h; y++) { // Loop over image rows fprintf(stderr, "\rRendering (%d spp) %5.2f%%", samps*4, 100.*y/(h-1)); for (unsigned short x=0, Xi[3]={0,0,(unsigned short)(y*y*y)}; x<w; x++) { // Loop cols for (int sy=0, i=(h-y-1)*w+x; sy<2; sy++) { // 2x2 subpixel rows for (int sx=0; sx<2; sx++, r=Vec()) { // 2x2 subpixel cols for (int s=0; s<samps; s++) { float r1=2*erand48(Xi), dx=r1<1 ? sqrt(r1)-1 : 1-sqrt(2-r1); float r2=2*erand48(Xi), dy=r2<1 ? sqrt(r2)-1 : 1-sqrt(2-r2); Vec d = cx*(((sx+.5+dx)/2+x)/w-.5)+cy*(((sy+.5+dy)/2+y)/h-.5)+cam.d; r = r + radiance(Ray(cam.o+d*140, d.norm()), 0, Xi)*(1./samps); } // Camera rays are pushed ^^^^^ forward to start in interior frame[i] = frame[i] + Vec(clamp(r.x), clamp(r.y), clamp(r.z))*.25; } } } } // Write image to PPM file. FILE *f = fopen("image.ppm", "wb"); fprintf(f, "P6\n%d %d\n%d\n", w, h, 255); for (int i=0; i<w*h; i++) fprintf(f, "%c%c%c", toInt(frame[i].x), toInt(frame[i].y), toInt(frame[i].z)); }
int main (int, char **) { QTime timer; timer.start(); int w = 768, h = 768; int samplesLight = 1; int samplesAliasing = 1; std::vector<glm::vec3> colors(w * h, glm::vec3{0.f, 0.f, 0.f}); Ray cam {{50, 52, 295.6}, glm::normalize(glm::vec3{0, -0.042612, -1})}; // cam pos, dir float near = 1.f; float far = 10000.f; glm::mat4 camera = glm::scale(glm::mat4(1.f), glm::vec3(float(w), float(h), 1.f)) * glm::translate(glm::mat4(1.f), glm::vec3(0.5, 0.5, 0.f)) * glm::perspective(float(54.5f * pi / 180.f), float(w) / float(h), near, far) * glm::lookAt(cam.origin, cam.origin + cam.direction, glm::vec3(0, 1, 0)) ; glm::mat4 screenToRay = glm::inverse(camera); #pragma omp parallel for schedule(dynamic) for (int y = 0; y < h; y++) { std::cerr << "\rRendering: " << 100 * y / (h - 1) << "%"; for (unsigned short x = 0; x < w; x++) { glm::vec3 r(0,0,0); for (int samplesA = 0; samplesA < samplesAliasing; ++samplesA) { float aleaX = random_u()-0.5; float aleaY = random_u()-0.5; glm::vec4 p0 = screenToRay * glm::vec4{float(x + aleaX), float(h - y + aleaY), 0.f, 1.f}; glm::vec4 p1 = screenToRay * glm::vec4{float(x + aleaX), float(h - y + aleaY), 1.f, 1.f}; glm::vec3 pp0 = glm::vec3(p0 / p0.w); glm::vec3 pp1 = glm::vec3(p1 / p1.w); glm::vec3 d = glm::normalize(pp1 - pp0); for (int samplesL = 0; samplesL < samplesLight; ++samplesL) { r += radiance (Ray{pp0, d}); } } r = r/float(samplesLight*samplesAliasing); colors[y * w + x] += glm::clamp(r, glm::vec3(0.f, 0.f, 0.f), glm::vec3(1.f, 1.f, 1.f));// * 0.25f; } } std::cout << "\n" << timer.elapsed()/1000.0 << "s" << std::endl; { std::fstream f("image.ppm", std::fstream::out); f << "P3\n" << w << " " << h << std::endl << "255" << std::endl; for (auto c : colors) f << toInt(c.x) << " " << toInt(c.y) << " " << toInt(c.z) << " "; } }
int main(int argc, char **argv) { int width = 640; int height = 480; int samples = 16; // カメラ位置 Ray camera(Vec(50.0, 52.0, 295.6), Normalize(Vec(0.0, -0.042612, -1.0))); // シーン内でのスクリーンのx,y方向のベクトル Vec cx = Vec(width * 0.5135 / height); Vec cy = Normalize(Cross(cx, camera.dir)) * 0.5135; Color *image = new Color[width * height]; Color *image_interpolated = new Color[width * height]; // フォームファクター計算 // 16x16に分割、フォームファクターの計算に(3x3)^2サンプル使ってモンテカルロ積分する calc_form_factor(16, 16, 3); // ガウス=ザイデル法でラジオシティ方程式解く // 32反復 for (int i = 0; i < 32; i ++) { std::cout << i << " "; calc_radiosity(i); } //#pragma omp parallel for schedule(dynamic, 1) num_threads(3) for (int y = 0; y < height; y ++) { std::cerr << "Rendering (" << samples * 4 << " spp) " << (100.0 * y / (height - 1)) << "%" << std::endl; srand(y * y * y); for (int x = 0; x < width; x ++) { int image_index = y * width + x; image[image_index] = Color(); image_interpolated[image_index] = Color(); // 2x2のサブピクセルサンプリング for (int sy = 0; sy < 2; sy ++) { for (int sx = 0; sx < 2; sx ++) { Color accumulated_radiance = Color(); Color accumulated_radiance2 = Color(); // 一つのサブピクセルあたりsamples回サンプリングする for (int s = 0; s < samples; s ++) { // テントフィルターによってサンプリング // ピクセル範囲で一様にサンプリングするのではなく、ピクセル中央付近にサンプルがたくさん集まるように偏りを生じさせる const double r1 = 2.0 * rand01(), dx = r1 < 1.0 ? sqrt(r1) - 1.0 : 1.0 - sqrt(2.0 - r1); const double r2 = 2.0 * rand01(), dy = r2 < 1.0 ? sqrt(r2) - 1.0 : 1.0 - sqrt(2.0 - r2); Vec dir = cx * (((sx + 0.5 + dx) / 2.0 + x) / width - 0.5) + cy * (((sy + 0.5 + dy) / 2.0 + y) / height- 0.5) + camera.dir; accumulated_radiance = accumulated_radiance + radiance(Ray(camera.org + dir * 130.0, Normalize(dir)), 0, false) / samples; accumulated_radiance2 = accumulated_radiance2 + radiance(Ray(camera.org + dir * 130.0, Normalize(dir)), 0, true) / samples; } image[image_index] = image[image_index] + accumulated_radiance; image_interpolated[image_index] = image_interpolated[image_index] + accumulated_radiance2; } } } } // .hdrフォーマットで出力 save_hdr_file(std::string("image.hdr"), image, width, height); save_hdr_file(std::string("image_interpolated.hdr"), image_interpolated, width, height); }
nex::color path_tracer::radiance(const sampler* sampler, nex::ray* ray) const { return radiance(sampler, ray, true); }
// ============================================================= // ============================================================= // ============================================================= int main() { printf("Small MCFTLE: Computing...\n"); // ============================================================= // initialize // ============================================================= Vec3d eye(1,0.5,2.3), lookAt(1,0.5,0.5), up(0,1,0); // camera parameters double fov = 45.0; Camera camera(eye, lookAt, up, fov, screenResolution); // pinhole camera Frame frame(screenResolution); // stores pixel statistics Flow flow; // vector field Timer timer; // timer to measure runtime // ============================================================= // progressive rendering // ============================================================= for (int it=0; it<maxIterationsPerPixel; ++it) { timer.tic(); // start runtime measurement #ifdef NDEBUG #pragma omp parallel for schedule(dynamic,16) #endif for (int px=0; px<screenResolution.x*screenResolution.y; ++px) { // select pixel and generate a ray Vec2i pxCoord(px%screenResolution.x, px/screenResolution.x); Ray viewRay = camera.GenerateRay(pxCoord); // intersect ray with domain (entry and exit) Vec2d t; if (!flow.IntersectRay(viewRay, t)) continue; // free path sampling to find a scattering event FreePathResult fprView = FreePathSampling(viewRay, flow); Vec3d radiance(1,1,1); // initialize with radiance of the background (white) // if we found a scattering event if (fprView.d <= fprView.dmax) { // compute the scattering albedo c Vec3d albedo = ScatteringAlbedo(fprView.ftle); // compute scattering location x_s and generate shadow ray Vec3d x_s = viewRay.Origin + fprView.d * viewRay.Direction; Ray shadowRay(x_s, -lightDirection); // intersect shadow ray with domain (determine exit) if (!flow.IntersectRay(shadowRay, t)) continue; // free path sampling to find a scattering event on the shadow ray FreePathResult fprLight = FreePathSampling(shadowRay, flow); // if we have scattered, we are in shadow. (actually visibility would be zero now, but for vis purposes we keep a little bit.) double visibility = 1; if (fprLight.d <= fprLight.dmax) visibility = visibility_minimum; // phase function double phase = 1.0 / (4.0 * M_PI); // isotropic phase function // compute in-scattered radiance radiance = albedo * phase * visibility * lightRadiance; } // add the result to the pixel statistics frame.AddPixel(pxCoord, radiance); } timer.toc(); // finish runtime measurement (prints result to console) // write intermediate result to file frame.ExportPfm("result.pfm"); frame.ExportBmp("result.bmp", contrast, brightness, gamma_val); printf("Iteration: %i / %i\n", it+1, maxIterationsPerPixel); } printf("\nComplete!\n"); return 0; }
glm::vec3 tracer::Pathtracer::radiance(const Ray& ray, std::mt19937& engine, unsigned depth, bool considerEmission, bool debug) { std::uniform_real_distribution<float> distribution(0.0f, 1.0f); float distance, minimumDistance = std::numeric_limits<float>::max(); Intersection tmp, isect; for (auto it = m_scene.begin(); it != m_scene.end(); ++it) { if ((*it)->intersect(ray, tmp)) { tmp.valid = true; if (debug) { std::cout << "testing intersection " << tmp.point << ", distance" << glm::distance2(ray.org, tmp.point) << std::endl; } if ((distance = glm::distance2(ray.org, tmp.point)) < minimumDistance) { minimumDistance = distance; isect = tmp; } } } if (isect.valid) { if (debug) { std::cout << "found intersection @ " << isect.point << ", normal: " << isect.normal << std::endl; } // russian roulette const material::Material& material = isect.object->getMaterial(); glm::vec3 objectColor = material.color; float p = MAX(objectColor.r, objectColor.g, objectColor.b); if (++depth > 5) { if (distribution(engine) < p) { objectColor = objectColor / p; } else { if (considerEmission) { return material.emission; } else { return glm::vec3(0.0f); } } } if (material.type == material::Material::Type_diffuse) { if (debug) { std::cout << "shading diffuse material" << std::endl; } // construct orthonormal system float r1 = 2 * M_PI * distribution(engine); float r2 = distribution(engine); float r2s = sqrt(r2); glm::vec3 w = isect.normal; glm::vec3 u = glm::normalize( glm::cross( (fabs(w.x) > 0.1f ? ey : ex), w ) ); glm::vec3 v = glm::normalize( glm::cross(w, u) ); glm::vec3 d = glm::normalize((float)(cos(r1) * r2s) * u + (float)(sin(r1) * r2s) * v + (float)sqrt(1-r2) * w); Ray scatteredRay(isect.point+epsilon*isect.normal, d); if (debug) { std::cout << "tracing next ray: " << scatteredRay << std::endl; } // compose output color glm::vec3 color = (considerEmission ? material.emission : glm::vec3(0.0f)); // add direct lighting contribution color += sampleDirectLighting(isect.point, isect.normal, objectColor, engine); // add indirect lighting contribution color += objectColor * radiance(scatteredRay, engine, depth, false, debug); return color; } else if (material.type == material::Material::Type_reflective) { if (debug) { std::cout << "shading reflective material" << std::endl; } Ray reflectedRay(isect.point+epsilon*isect.normal, glm::reflect(ray.dir, isect.normal)); return material.emission + objectColor * radiance(reflectedRay, engine, depth, true, debug); } else { error("Unhandled material type detected"); return m_scene.getBackgroundColor(); } } else { if (debug) { std::cout << "found no intersection" << std::endl; } return m_scene.getBackgroundColor(); } }
Colorf Sky::getRadianceAt(float theta, float phi, bool checkerboard) { if(checkerboard) { const int x = theta / (pi / 5); const int y = phi / (pi / 5); const int parity = (x ^ y) % 2; const float val = parity ? 150 : 100; return Colorf(val, val, val); } if(theta > pi / 2) { return Colorf(0, 0, 0); } // Preetham sky model // http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf // // Hints for reading the paper: // * we don't consider object light scattering // (maybe needed later when considering far-away moutains or such) // * when there's no object, L(0) = 0 (universe background) // * don't get distracted by approximations for hand-calculation // (it's simpler (and more flexible) to do numerical calc + smart memoization) const float view_height = 0; const float alpha_haze = 0.8333; // haze: /km const float alpha_mole = 0.1136; // molecules: /km const Eigen::Vector3f view_direction( std::sin(theta) * std::cos(phi), std::sin(theta) * std::sin(phi), std::cos(theta)); // Based on Nishita's 1st-order scattering sky model. // We ignore point-to-space decay. Colorf radiance(0, 0, 0); Colorf decay_here_to_view(1, 1, 1); for(int i = 0; i < 50; i++) { const float distance = i; const float height = distance * std::cos(theta) + view_height; // const float cos_view_sun = view_direction.dot(sun_direction); // Calculate scattering // Mie scattering's phase function is not well-described anywhere. // But it's said to be very sharp. So // we use (1+cos^3 theta)^2/4 // (http://www.wolframalpha.com/input/?i=plot+r%3D%281%2Bcos%5E3+theta%29%5E2%2F4) const Colorf scatter = particleDensity(alpha_mole, distance, theta) * rayleigh(cos_view_sun) + particleDensity(alpha_haze, distance, theta) * mie(cos_view_sun); // Note. decay_space_to_here and decay_here_to_view are not colinear. radiance += sun_power .cwiseProduct(scatter) .cwiseProduct(decay_here_to_view); const Colorf decay_scatter = particleDensity(alpha_mole, distance, theta) * rayleighTotal() + particleDensity(alpha_haze, distance, theta) * mieTotal(); decay_here_to_view = decay_here_to_view.cwiseProduct( Colorf(1, 1, 1) - decay_scatter); } assert(radiance[0] >= 0 && radiance[1] >= 0 && radiance[2] >= 0); radiance /= 100; return radiance; }
RGB SubsurfacePathtracer::trace(Ray p_ray, Vector3d cameraPos, int p_depth) { RGB radiance(0,0,0); RGB localRadiance(0,0,0); RayIntersection nearestRayInt; p_depth++; bool intersected = false; if(scene->kdTree == NULL) { intersected = p_ray.nearestIntersection(scene->getObjects(), scene->getObjectCount(), &nearestRayInt); } else { intersected = scene->kdTree->nearestIntersection(&p_ray, &nearestRayInt); } if(intersected) { if(!(nearestRayInt.object->getShader()->isLight() && p_ray.getDiffuse())) { //local ilumination: Vector3d viewVec = cameraPos - nearestRayInt.point; viewVec.normalize(); localRadiance = nearestRayInt.object->getShader()->getDiffuseRadiance(nearestRayInt.object, nearestRayInt.point, nearestRayInt.normal, nearestRayInt.uv, viewVec, scene); radiance = localRadiance; if(nearestRayInt.object->getShader()->hasSubsurface()) { radiance = radiance + subsurface(&nearestRayInt, &p_ray); //return subsurface(&nearestRayInt, &p_ray); } int outVecsCount = 0; RGB indirectRadiance; if(p_depth == 1) { for(int i = 0; i < 5; i++) { if(p_depth < maxDepth) { Ray outRays[10]; RGB values[10]; outVecsCount = nearestRayInt.object->getShader()->getBRDFSampledRays(nearestRayInt.object, nearestRayInt.point, nearestRayInt.normal, nearestRayInt.uv, viewVec, p_ray.getDir(), outRays, values); for(int s = 0; s < outVecsCount; s++) { Ray ray = outRays[s]; indirectRadiance = indirectRadiance + (trace(ray, cameraPos, p_depth) * values[s]) ; } } indirectRadiance; } indirectRadiance = indirectRadiance / 5; } else { if(p_depth < maxDepth) { Ray outRays[10]; RGB values[10]; outVecsCount = nearestRayInt.object->getShader()->getBRDFSampledRays(nearestRayInt.object, nearestRayInt.point, nearestRayInt.normal, nearestRayInt.uv, viewVec, p_ray.getDir(), outRays, values); for(int s = 0; s < outVecsCount; s++) { Ray ray = outRays[s]; indirectRadiance = indirectRadiance + (trace(ray, cameraPos, p_depth) * values[s]) ; } } } if(outVecsCount > 0) { radiance = radiance + indirectRadiance; } } } else if(scene->getBackground() != NULL) { radiance = scene->getBackground()->getRadianceAt(p_ray.getPoint(), p_ray.getPointAt(FAR_AWAY)); } return radiance; }
Imath::C3f radiance(Scene& scene, Ray& ray, Imath::Rand48& rnd, const int depth) { Imath::V3d intersection_point, intersection_normal; int obj_id; if (!scene.intersect(ray, intersection_point, intersection_normal, obj_id)) { return scene.bgColor; } Geometry geom = scene.geometries[obj_id]; Imath::V3d orienting_normal = intersection_normal; if ((intersection_normal ^ ray.dir) > 0.0) { orienting_normal *= -1.0; } if (depth > LIMIT_DEPTH) { return geom.emission; } float reflect_ratio = (5.0f - static_cast<float>(depth)) / 5.0f; Imath::C3f inc_rad(0,0,0), weight(1,1,1); switch (geom.reflection) { case Reflection::Diffuse: { Imath::V3d w, u, v; w = orienting_normal; if (fabs(w.x) > 0.0000009) { u = (Imath::V3d(0.0, 1.0, 0.0) % w).normalized(); } else { u = (Imath::V3d(1.0, 0.0, 0.0) % w).normalized(); } v = w % u; double r1 = 2.0 * M_PI * rnd.nextf(); double r2 = rnd.nextf(); double rr2 = sqrt(r2); ray.pos = intersection_point; ray.dir = (u * cos(r1) * rr2 + v * sin(r1) * rr2 + w * sqrt(1.0 - r2)).normalized(); inc_rad = radiance(scene, ray, rnd, depth + 1); weight = geom.color * reflect_ratio; } break; case Reflection::Specular: ray.pos = intersection_point; ray.dir -= intersection_normal * 2.0 * (intersection_normal ^ ray.dir); inc_rad = radiance(scene, ray, rnd, depth + 1); weight = geom.color * reflect_ratio; break; case Reflection::Refraction: bool into = (orienting_normal ^ intersection_normal) > 0.0; double default_refraction = 1.0; double object_refraction = 1.5; double ray_refraction; if (into) { ray_refraction = default_refraction / object_refraction; } else { ray_refraction = object_refraction / default_refraction; } double incident_dot = ray.dir ^ orienting_normal; double critical_factor = 1.0 - pow(ray_refraction, 2) * (1.0 - pow(incident_dot,2)); Ray reflection_ray(intersection_point, ray.dir - intersection_normal * 2.0 * (intersection_normal ^ ray.dir)); Ray refraction_ray(intersection_point, (ray.dir * ray_refraction - intersection_normal * (into ? 1.0 : -1.0) * (incident_dot * ray_refraction + sqrt(critical_factor))).normalized()); // total reflection if (critical_factor < 0.0) { inc_rad = radiance(scene, reflection_ray, rnd, depth + 1); weight = geom.color * reflect_ratio; break; } double a = object_refraction - default_refraction; double b = object_refraction + default_refraction; double vertical_incidence_factor = pow(a, 2) / pow(b, 2); double c = 1.0 - (into ? -1.0 * incident_dot : (refraction_ray.dir ^ -1.0 * orienting_normal)); double fresnel_incidence_factor = vertical_incidence_factor + (1.0 - vertical_incidence_factor) * pow(c,5); double radiance_scale = pow(ray_refraction, 2.0); double refraction_factor = (1.0 - fresnel_incidence_factor) * radiance_scale; double probability = 0.75 + fresnel_incidence_factor; if (depth > 2) { if (rnd.nextf() < probability) { inc_rad = radiance(scene, reflection_ray, rnd, depth + 1) * fresnel_incidence_factor; weight = geom.color * reflect_ratio; } else { inc_rad = radiance(scene, refraction_ray, rnd, depth + 1) * refraction_factor; weight = geom.color * reflect_ratio; } } else { inc_rad = radiance(scene, reflection_ray, rnd, depth + 1) * fresnel_incidence_factor + radiance(scene, refraction_ray, rnd, depth + 1) * refraction_factor; weight = geom.color * reflect_ratio; } break; } return geom.emission + weight * inc_rad; }