Beispiel #1
0
Float3 radiance_gdpt(Random& random, Random& random2, const SimpleVolume& volume, const rt::Ray& initial_ray, std::vector<Vertex>* vertices, float *path_pdf, Color *finalL) {
    const int kMaxScattering = 256;

    rt::Ray current_ray = initial_ray;

    Color througput(1, 1, 1);
    Color L(0, 0, 0);


    for (int scattering = 0; scattering < kMaxScattering; ++scattering) {
        // VolumeのBBoxにヒットするか否か
        float hitt0 = -1, hitt1 = -1;
        bool is_volume_hit = false;
        if (volume.check_intersect(current_ray, &hitt0, &hitt1)) {
            is_volume_hit = true;
        }

        rt::Hitpoint current_hp;
        if (!volume.inside(current_ray.org)) {
            if (is_volume_hit) {
                current_hp.distance = hitt0;
            }
        }

        if (!is_volume_hit) {
            L += outside(current_ray);
            break;
        }

        const Float3 new_org0 = current_ray.org + (hitt0 + 1e-3f) * current_ray.dir;
        const Float3 new_org1 = current_ray.org + (hitt1 + 1e-3f) * current_ray.dir;
        if (!volume.inside(current_ray.org)) {
            // Volumeの外だったら中からスタート
            current_ray.org = new_org0;
        }
        if (scattering == 0) {
            // 一個目の頂点を入れる
            vertices->push_back(Vertex(current_ray.org, current_ray.dir, 1));
        }
        const float tu = volume.next_event(current_ray, random);
        if (tu < 0) {
            // ボリューム外
            current_ray.org = new_org1;
            continue;
        }

        // 散乱した!

        // pdf_transmittance
        float transmittance = 0;
        const int kNumSample = 16;
        /*
        {
            float sum = 0;
            for (int i = 0; i < kNumSample; ++i) {
                const float length = tu;

                const float current = length / kNumSample * (i + random.next01());

                sum += volume.sample_transmittance(current_ray.org + current * current_ray.dir) / kNumSample;
            }

            transmittance = exp(-sum);
        }*/
        for (int i = 0; i < kNumSample; ++i) {
            const float X = volume.next_event(current_ray, random2);
            if (!(0 <= X && X < tu)) {
                transmittance += 1.0f / kNumSample;
            }
        }

        const Float3 next_pos = current_ray.org + tu * current_ray.dir;
        const bool cond2 = random.next01() < kAlbedo;
        if (!cond2) {
            *path_pdf = 0;
            througput = Float3(0, 0, 0); // absorp
            break;
        }

        // 位相関数でインポータンスサンプリング
        // 今回はとりあえず等方散乱としておく
        const Float3 next_dir = Sampling::uniformSphereSurface(random);
        const float phase = 1.0f / (4.0f * kPI);
        const float pdf_phase = 1.0f / (4.0f * kPI);

        /*
        througput *= 0.99f;
        *path_pdf *= 0.99f;
        */

        througput *= transmittance * phase * kAlbedo;
        *path_pdf *= transmittance * pdf_phase * kAlbedo;


        // 頂点追加
        vertices->push_back(Vertex(next_pos, next_dir, transmittance));


        // throughtput = (phase / pdf) * throughtput;
        current_ray = rt::Ray(next_pos, next_dir);
    }
    *finalL = L;

    return times(L, througput);
}
Beispiel #2
0
void render_gdpt(const char *filename, const int width, const int height, const int num_sample_per_subpixel, const int num_thread, unsigned long long seed,
                 const SimpleVolume& ovolume,
                 const rt::PerspectiveCamera& camera) {

    std::vector<Color> coarse_image(width * height);
    std::vector<Color> debug_image(width * height);
    std::vector<Color> diff_image[4];
    for (int i = 0; i < 4; ++i)
        diff_image[i].resize(width * height);

    Timer timer;
    timer.begin();

    for (int iy = 0; iy < height; ++iy) {
        std::cout << "y: " << iy << std::endl;
        #pragma omp parallel for schedule(dynamic, 1) num_threads(8)
        for (int ix = 0; ix < width; ++ix) {
            Random random((unsigned long long)(iy * width + ix));

            const int image_index = iy * width + ix;
            for (int ss = 0; ss < num_sample_per_subpixel; ++ss) {
                float sx = random.next01();
                float sy = random.next01();

                std::vector<Vertex> baseVertices;
                baseVertices.reserve(16);
                float basePathPDF = 1;
                Color baseContritbuion;
                Color finalL;

                float baseX = ix + sx;
                float baseY = iy + sy;
                const unsigned long long random2_seed = (unsigned long long)(iy * width + ix) * num_sample_per_subpixel + ss;
                rt::Ray base_ray;
                {
                    const float u = baseX / width;
                    const float v = baseY / height;
                    base_ray = camera.get_ray(u, v);
                    baseContritbuion = radiance_gdpt(random, random, ovolume, base_ray, &baseVertices, &basePathPDF, &finalL);
                }

                if (basePathPDF > 0)
                    coarse_image[image_index] += baseContritbuion / basePathPDF;
                if (basePathPDF <= 0)
                    continue;
                // 4 sub-path
                const Float3 offset[4] = {
                    Float3( 1, 0, 0),
                    Float3(-1, 0, 0),
                    Float3( 0, 1, 0),
                    Float3( 0,-1, 0)
                };
                std::vector<Vertex> offsetVertices[4];
                for (int offsetDir = 0; offsetDir < 4; ++offsetDir) {
                    const float u = (baseX + offset[offsetDir].x) / width;
                    const float v = (baseY + offset[offsetDir].y) / height;
                    const rt::Ray offset_ray = camera.get_ray(u, v);
                    offsetVertices[offsetDir].reserve(16);

                    enum Result {
                        Naive,
                        Invertible
                    };

                    // Shift
                    rt::Ray current_ray = offset_ray;
                    Result result = Naive;

                    // baseが少なくとも2回は散乱している、有効なパスのときのみ考える
                    Color offsetContribution;
                    float J = 0;
                    if (basePathPDF > 0 && baseVertices.size() >= 3) {
                        const float initial_length = (baseVertices[1].position - base_ray.org).length();

                        const Float3 offset_dir0 = current_ray.dir;
                        const Float3 offset_1 = current_ray.org + initial_length * offset_dir0;

                        if (!ovolume.inside(offset_1)) {
                            // offsetが外に出てしまったので、Naiveにやる
                        } else {
                            result = Invertible;
                            float hitt0 = -1, hitt1 = -1;
                            ovolume.check_intersect(current_ray, &hitt0, &hitt1);

                            const Float3 new_org0 = current_ray.org + (hitt0 + 1e-3f) * offset_dir0;
                            const Float3 new_org1 = current_ray.org + (hitt1 + 1e-3f) * offset_dir0;
                            if (!ovolume.inside(current_ray.org)) {
                                // Volumeの外だったら中からスタート
                                current_ray.org = new_org0;
                            }
                            const Float3 offset_0 = current_ray.org;



                            // Baseの3個目の頂点につなげる
                            const Float3 offset_2 = baseVertices[2].position;

                            // 0 - 1と 1- 2の間のtransmittanceを求める
                            const rt::Ray ray01(offset_0, normalize(offset_1 - offset_0));
                            const rt::Ray ray12(offset_1, normalize(offset_2 - offset_1));
                            const float length01 = (offset_0 - offset_1).length();
                            const float length12 = (offset_1 - offset_2).length();
                            float transmittance01 = 0;
                            float transmittance12 = 0;
                            const int kNumSample = 16;


                            /*
                            {
                                float sum = 0;
                                for (int i = 0; i < kNumSample; ++i) {
                                    const float current = length01 / kNumSample * (i + random.next01());
                                    sum += ovolume.sample_transmittance(ray01.org + current * ray01.dir) / kNumSample;
                                }
                                transmittance01 = exp(-sum);
                            }
                            {
                                float sum = 0;
                                for (int i = 0; i < kNumSample; ++i) {
                                    const float current = length12 / kNumSample * (i + random.next01());
                                    sum += ovolume.sample_transmittance(ray12.org + current * ray12.dir) / kNumSample;
                                }
                                transmittance12 = exp(-sum);
                            }
                            */
                            for (int i = 0; i < kNumSample; ++i) {
                                const float X = ovolume.next_event(ray01, random);
                                if (!(0 <= X && X < length01)) {
                                    transmittance01 += 1.0f / kNumSample;
                                }

                            }
                            /*
                                if (baseVertices[1].transmittance != transmittance01)
                                    printf("%f %f\n", baseVertices[1].transmittance, transmittance01);
                                */
                            for (int i = 0; i < kNumSample; ++i) {
                                const float X = ovolume.next_event(ray12, random);
                                if (!(0 <= X && X < length12)) {
                                    transmittance12 += 1.0f / kNumSample;
                                }
                            }

                            // ヤコビアン計算
                            J = (baseVertices[2].position - baseVertices[1].position).lengthSquared() /
                                (offset_2 - offset_1).lengthSquared();

                            // 残りのTransmittanceはBaseと同じ!
                            float total_transmittance = transmittance01 * transmittance12;
                            for (int i = 3; i < baseVertices.size(); ++i)
                                total_transmittance *= baseVertices[i].transmittance;

                            // その他の要素
                            const float phase = 1.0f / (4.0f * kPI);
                            const int numScattering = baseVertices.size() - 1;

                            const float throughput = total_transmittance * pow(phase * kAlbedo, numScattering);

                            offsetContribution = throughput * finalL;

                        }
                    }
                    Color accum;
                    if (result == Naive) {
                        /*
                        if (iy > 100)
                        printf("%f %d\n", basePathPDF, baseVertices.size());
                        */

                        float pdf = 1;
                        const Float3 contribution = radiance(random, ovolume, offset_ray, &pdf);
                        if (pdf > 0) {
                            accum = baseContritbuion / basePathPDF - contribution / pdf;
                        } else {
                            accum = baseContritbuion / basePathPDF;
                        }


                        /*
                        if (ix == 320 && iy == 240) {
                            printf("%f %f %f %f\n", baseContritbuion.x, basePathPDF, contribution.x, pdf);
                            std::cout << accum << std::endl;
                        }
                        */
                    } else {
                        // printf("%f %f %f\n", baseContritbuion.x, offsetContribution.x, J);
                        accum = 0.5f * (baseContritbuion - offsetContribution * J) / basePathPDF;

                    }

                    // 前方差分 or 後方差分
                    if (offsetDir == 1 || offsetDir == 2)
                        diff_image[offsetDir][image_index] += accum;
                    else
                        diff_image[offsetDir][image_index] += -accum;
                }
            }
            coarse_image[image_index] = coarse_image[image_index] / num_sample_per_subpixel;
            for (int i = 0; i < 4; ++i)
                diff_image[i][image_index] = diff_image[i][image_index] / num_sample_per_subpixel;
        }
    }

    std::cout << "Time: " << (timer.end() / 1000.0f) << " sec" << std::endl;

    char buf[256];
    char name[4][256] = {
        "__1_0",
        "_-1_0",
        "__0_1",
        "__0-1"
    };
    sprintf(buf, "%s_J.hdr", filename);
    save_hdr_file(buf, &debug_image[0], width, height);

    sprintf(buf, "%s_coarse.hdr", filename);
    save_hdr_file(buf, &coarse_image[0], width, height);
    sprintf(buf, "%s_coarse.bin", filename);
    save_binary_file(buf, &coarse_image[0], width, height);

    for (int i = 0; i < 4; ++i) {
        sprintf(buf, "%s_%s.hdr", filename, name[i]);
        save_hdr_file(buf, &diff_image[i][0], width, height);
        sprintf(buf, "%s_%s.bin", filename, name[i]);
        save_binary_file(buf, &diff_image[i][0], width, height);
    }
}
Beispiel #3
0
Float3 radiance(Random& random, const SimpleVolume& volume, const rt::Ray& initial_ray, float *path_pdf) {
    const int kMaxScattering = 256;

    rt::Ray current_ray = initial_ray;

    Color througput(1, 1, 1);
    Color L(0, 0, 0);
    for (int scattering = 0; scattering < kMaxScattering; ++scattering) {
        // VolumeのBBoxにヒットするか否か
        float hitt0 = -1, hitt1 = -1;
        bool is_volume_hit = false;
        if (volume.check_intersect(current_ray, &hitt0, &hitt1)) {
            is_volume_hit = true;
        }

        rt::Hitpoint current_hp;
        if (!volume.inside(current_ray.org)) {
            if (is_volume_hit) {
                current_hp.distance = hitt0;
            }
        }

        if (!is_volume_hit) {
            L += outside(current_ray);
            break;
        }

        const Float3 new_org0 = current_ray.org + (hitt0 + 1e-3f) * current_ray.dir;
        const Float3 new_org1 = current_ray.org + (hitt1 + 1e-3f) * current_ray.dir;
        if (!volume.inside(current_ray.org)) {
            // Volumeの外だったら中からスタート
            current_ray.org = new_org0;
        }

        const float tu = volume.next_event(current_ray, random);
        if (tu < 0) {
            // ボリューム外
            current_ray.org = new_org1;
            continue;
        }

        // pdf_transmittance
        float transmittance = 0;
        const int kNumSample = 16;
        for (int i = 0; i < kNumSample; ++i) {
            const float X = volume.next_event(current_ray, random);
            if (!(0 <= X && X < tu)) {
                transmittance += 1.0f / kNumSample;
            }
        }

        const Float3 next_pos = current_ray.org + tu * current_ray.dir;
        const bool cond2 = random.next01() < kAlbedo;
        if (!cond2) {
            througput = Float3(0, 0, 0); // absorp
            break;
        }


        // 位相関数でインポータンスサンプリング
        // 今回はとりあえず等方散乱としておく
        const Float3 next_dir = Sampling::uniformSphereSurface(random);
        const float phase = 1.0f / (4.0f * kPI);
        const float pdf_phase = 1.0f / (4.0f * kPI);

        /*
        througput *= 0.99f;
        *path_pdf *= 0.99f;
        */

        througput *= transmittance * phase * kAlbedo;
        *path_pdf *= transmittance * pdf_phase * kAlbedo;



        // throughtput = (phase / pdf) * throughtput;
        current_ray = rt::Ray(next_pos, next_dir);
    }

    return times(L, througput);
}