Team Ai
Apppublic

CVPR/LIVE

sourceHugging Facegpl-3.0updated 4y agoView on Hugging Face
34likes
diffvg.cpp1793 linesDownload Raw Back to root
1#include "diffvg.h"2#include "aabb.h"3#include "shape.h"4#include "sample_boundary.h"5#include "atomic.h"6#include "cdf.h"7#include "compute_distance.h"8#include "cuda_utils.h"9#include "edge_query.h"10#include "filter.h"11#include "matrix.h"12#include "parallel.h"13#include "pcg.h"14#include "ptr.h"15#include "scene.h"16#include "vector.h"17#include "winding_number.h"18#include "within_distance.h"19#include <cassert>20#include <pybind11/pybind11.h>21#include <pybind11/stl.h>22#include <thrust/execution_policy.h>23#include <thrust/sort.h>24 25namespace py = pybind11;26 27struct Command {28    int shape_group_id;29    int shape_id;30    int point_id; // Only used by path31};32 33DEVICE34bool is_inside(const SceneData &scene_data,35               int shape_group_id,36               const Vector2f &pt,37               EdgeQuery *edge_query) {38    const ShapeGroup &shape_group = scene_data.shape_groups[shape_group_id];39    // pt is in canvas space, transform it to shape's local space40    auto local_pt = xform_pt(shape_group.canvas_to_shape, pt);41    const auto &bvh_nodes = scene_data.shape_groups_bvh_nodes[shape_group_id];42    const AABB &bbox = bvh_nodes[2 * shape_group.num_shapes - 2].box;43    if (!inside(bbox, local_pt)) {44        return false;45    }46    auto winding_number = 0;47    // Traverse the shape group BVH48    constexpr auto max_bvh_stack_size = 64;49    int bvh_stack[max_bvh_stack_size];50    auto stack_size = 0;51    bvh_stack[stack_size++] = 2 * shape_group.num_shapes - 2;52    while (stack_size > 0) {53        const BVHNode &node = bvh_nodes[bvh_stack[--stack_size]];54        if (node.child1 < 0) {55            // leaf56            auto shape_id = node.child0;57            auto w = compute_winding_number(58                scene_data.shapes[shape_id], scene_data.path_bvhs[shape_id], local_pt);59            winding_number += w;60            if (edge_query != nullptr) {61                if (edge_query->shape_group_id == shape_group_id &&62                        edge_query->shape_id == shape_id) {63                    if ((shape_group.use_even_odd_rule && abs(w) % 2 == 1) ||64                        (!shape_group.use_even_odd_rule && w != 0)) {65                        edge_query->hit = true;66                    }67                }68            }69        } else {70            assert(node.child0 >= 0 && node.child1 >= 0);71            const AABB &b0 = bvh_nodes[node.child0].box;72            if (inside(b0, local_pt)) {73                bvh_stack[stack_size++] = node.child0;74            }75            const AABB &b1 = bvh_nodes[node.child1].box;76            if (inside(b1, local_pt)) {77                bvh_stack[stack_size++] = node.child1;78            }79            assert(stack_size <= max_bvh_stack_size);80        }81    }82    if (shape_group.use_even_odd_rule) {83        return abs(winding_number) % 2 == 1;84    } else {85        return winding_number != 0;86    }87}88 89DEVICE void accumulate_boundary_gradient(const Shape &shape,90                                         float contrib,91                                         float t,92                                         const Vector2f &normal,93                                         const BoundaryData &boundary_data,94                                         Shape &d_shape,95                                         const Matrix3x3f &shape_to_canvas,96                                         const Vector2f &local_boundary_pt,97                                         Matrix3x3f &d_shape_to_canvas) {98    assert(isfinite(contrib));99    assert(isfinite(normal));100    // According to Reynold transport theorem,101    // the Jacobian of the boundary integral is dot(velocity, normal),102    // where the velocity depends on the variable being differentiated with.103    if (boundary_data.is_stroke) {104        auto has_path_thickness = false;105        if (shape.type == ShapeType::Path) {106            const Path &path = *(const Path *)shape.ptr;107            has_path_thickness = path.thickness != nullptr;108        }109        // differentiate stroke width: velocity is the same as normal110        if (has_path_thickness) {111            Path *d_p = (Path*)d_shape.ptr;112            auto base_point_id = boundary_data.path.base_point_id;113            auto point_id = boundary_data.path.point_id;114            auto t = boundary_data.path.t;115            const Path &path = *(const Path *)shape.ptr;116            if (path.num_control_points[base_point_id] == 0) {117                // Straight line118                auto i0 = point_id;119                auto i1 = (point_id + 1) % path.num_points;120                // r = r0 + t * (r1 - r0)121                atomic_add(&d_p->thickness[i0], (1 - t) * contrib);122                atomic_add(&d_p->thickness[i1], (    t) * contrib);123            } else if (path.num_control_points[base_point_id] == 1) {124                // Quadratic Bezier curve125                auto i0 = point_id;126                auto i1 = point_id + 1;127                auto i2 = (point_id + 2) % path.num_points;128                // r = (1-t)^2r0 + 2(1-t)t r1 + t^2 r2129                atomic_add(&d_p->thickness[i0], square(1 - t) * contrib);130                atomic_add(&d_p->thickness[i1], (2*(1-t)*t) * contrib);131                atomic_add(&d_p->thickness[i2], (t*t) * contrib);132            } else if (path.num_control_points[base_point_id] == 2) {133                auto i0 = point_id;134                auto i1 = point_id + 1;135                auto i2 = point_id + 2;136                auto i3 = (point_id + 3) % path.num_points;137                // r = (1-t)^3r0 + 3*(1-t)^2tr1 + 3*(1-t)t^2r2 + t^3r3138                atomic_add(&d_p->thickness[i0], cubic(1 - t) * contrib);139                atomic_add(&d_p->thickness[i1], 3 * square(1 - t) * t * contrib);140                atomic_add(&d_p->thickness[i2], 3 * (1 - t) * t * t * contrib);141                atomic_add(&d_p->thickness[i3], t * t * t * contrib);142            } else {143                assert(false);144            }145        } else {146            atomic_add(&d_shape.stroke_width, contrib);147        }148    }149    switch (shape.type) {150        case ShapeType::Circle: {151            Circle *d_p = (Circle*)d_shape.ptr;152            // velocity for the center is (1, 0) for x and (0, 1) for y153            atomic_add(&d_p->center[0], normal * contrib);154            // velocity for the radius is the same as the normal155            atomic_add(&d_p->radius, contrib);156            break;157        } case ShapeType::Ellipse: {158            Ellipse *d_p = (Ellipse*)d_shape.ptr;159            // velocity for the center is (1, 0) for x and (0, 1) for y160            atomic_add(&d_p->center[0], normal * contrib);161            // velocity for the radius:162            // x = center.x + r.x * cos(2pi * t)163            // y = center.y + r.y * sin(2pi * t)164            // for r.x: (cos(2pi * t), 0)165            // for r.y: (0, sin(2pi * t))166            atomic_add(&d_p->radius.x, cos(2 * float(M_PI) * t) * normal.x * contrib);167            atomic_add(&d_p->radius.y, sin(2 * float(M_PI) * t) * normal.y * contrib);168            break;169        } case ShapeType::Path: {170            Path *d_p = (Path*)d_shape.ptr;171            auto base_point_id = boundary_data.path.base_point_id;172            auto point_id = boundary_data.path.point_id;173            auto t = boundary_data.path.t;174            const Path &path = *(const Path *)shape.ptr;175            if (path.num_control_points[base_point_id] == 0) {176                // Straight line177                auto i0 = point_id;178                auto i1 = (point_id + 1) % path.num_points;179                // pt = p0 + t * (p1 - p0)180                // velocity for p0.x: (1 - t,     0)181                //              p0.y: (    0, 1 - t)182                //              p1.x: (    t,     0)183                //              p1.y: (    0,     t)184                atomic_add(&d_p->points[2 * i0 + 0], (1 - t) * normal.x * contrib);185                atomic_add(&d_p->points[2 * i0 + 1], (1 - t) * normal.y * contrib);186                atomic_add(&d_p->points[2 * i1 + 0], (    t) * normal.x * contrib);187                atomic_add(&d_p->points[2 * i1 + 1], (    t) * normal.y * contrib);188            } else if (path.num_control_points[base_point_id] == 1) {189                // Quadratic Bezier curve190                auto i0 = point_id;191                auto i1 = point_id + 1;192                auto i2 = (point_id + 2) % path.num_points;193                // pt = (1-t)^2p0 + 2(1-t)t p1 + t^2 p2194                // velocity for p0.x: ((1-t)^2,       0)195                //              p0.y: (      0, (1-t)^2)196                //              p1.x: (2(1-t)t,       0)197                //              p1.y: (      0, 2(1-t)t)198                //              p1.x: (    t^2,       0)199                //              p1.y: (      0,     t^2)200                atomic_add(&d_p->points[2 * i0 + 0], square(1 - t) * normal.x * contrib);201                atomic_add(&d_p->points[2 * i0 + 1], square(1 - t) * normal.y * contrib);202                atomic_add(&d_p->points[2 * i1 + 0], (2*(1-t)*t) * normal.x * contrib);203                atomic_add(&d_p->points[2 * i1 + 1], (2*(1-t)*t) * normal.y * contrib);204                atomic_add(&d_p->points[2 * i2 + 0], (t*t) * normal.x * contrib);205                atomic_add(&d_p->points[2 * i2 + 1], (t*t) * normal.y * contrib);206            } else if (path.num_control_points[base_point_id] == 2) {207                auto i0 = point_id;208                auto i1 = point_id + 1;209                auto i2 = point_id + 2;210                auto i3 = (point_id + 3) % path.num_points;211                // pt = (1-t)^3p0 + 3*(1-t)^2tp1 + 3*(1-t)t^2p2 + t^3p3212                // velocity for p0.x: (   (1-t)^3,          0)213                //              p0.y: (         0,    (1-t)^3)214                //              p1.x: (3*(1-t)^2t,          0)215                //              p1.y: (         0, 3*(1-t)^2t)216                //              p2.x: (3*(1-t)t^2,          0)217                //              p2.y: (         0, 3*(1-t)t^2)218                //              p2.x: (       t^3,          0)219                //              p2.y: (         0,        t^3)220                atomic_add(&d_p->points[2 * i0 + 0], cubic(1 - t) * normal.x * contrib);221                atomic_add(&d_p->points[2 * i0 + 1], cubic(1 - t) * normal.y * contrib);222                atomic_add(&d_p->points[2 * i1 + 0], 3 * square(1 - t) * t * normal.x * contrib);223                atomic_add(&d_p->points[2 * i1 + 1], 3 * square(1 - t) * t * normal.y * contrib);224                atomic_add(&d_p->points[2 * i2 + 0], 3 * (1 - t) * t * t * normal.x * contrib);225                atomic_add(&d_p->points[2 * i2 + 1], 3 * (1 - t) * t * t * normal.y * contrib);226                atomic_add(&d_p->points[2 * i3 + 0], t * t * t * normal.x * contrib);227                atomic_add(&d_p->points[2 * i3 + 1], t * t * t * normal.y * contrib);228            } else {229                assert(false);230            }231            break;232        } case ShapeType::Rect: {233            Rect *d_p = (Rect*)d_shape.ptr;234            // The velocity depends on the position of the boundary235            if (normal == Vector2f{-1, 0}) {236                // left237                // velocity for p_min is (1, 0) for x and (0, 0) for y238                atomic_add(&d_p->p_min.x, -contrib);239            } else if (normal == Vector2f{1, 0}) {240                // right241                // velocity for p_max is (1, 0) for x and (0, 0) for y242                atomic_add(&d_p->p_max.x, contrib);243            } else if (normal == Vector2f{0, -1}) {244                // top245                // velocity for p_min is (0, 0) for x and (0, 1) for y246                atomic_add(&d_p->p_min.y, -contrib);247            } else if (normal == Vector2f{0, 1}) {248                // bottom249                // velocity for p_max is (0, 0) for x and (0, 1) for y250                atomic_add(&d_p->p_max.y, contrib);251            } else {252                // incorrect normal assignment?253                assert(false);254            }255            break;256        } default: {257            assert(false);258            break;259        }260    }261    // for shape_to_canvas we have the following relationship:262    // boundary_pt = xform_pt(shape_to_canvas, local_pt)263    // the velocity is the derivative of boundary_pt with respect to shape_to_canvas264    // we can use reverse-mode AD to compute the dot product of the velocity and the Jacobian265    // by passing the normal in d_xform_pt266    auto d_shape_to_canvas_ = Matrix3x3f();267    auto d_local_boundary_pt = Vector2f{0, 0};268    d_xform_pt(shape_to_canvas,269               local_boundary_pt,270               normal * contrib,271               d_shape_to_canvas_,272               d_local_boundary_pt);273    atomic_add(&d_shape_to_canvas(0, 0), d_shape_to_canvas_);274}275 276DEVICE277Vector4f sample_color(const ColorType &color_type,278                      void *color,279                      const Vector2f &pt) {280    switch (color_type) {281        case ColorType::Constant: {282            auto c = (const Constant*)color;283            assert(isfinite(c->color));284            return c->color;285        } case ColorType::LinearGradient: {286            auto c = (const LinearGradient*)color;287            // Project pt to (c->begin, c->end)288            auto beg = c->begin;289            auto end = c->end;290            auto t = dot(pt - beg, end - beg) / max(dot(end - beg, end - beg), 1e-3f);291            // Find the correponding stop:292            if (t < c->stop_offsets[0]) {293                return Vector4f{c->stop_colors[0],294                                c->stop_colors[1],295                                c->stop_colors[2],296                                c->stop_colors[3]};297            }298            for (int i = 0; i < c->num_stops - 1; i++) {299                auto offset_curr = c->stop_offsets[i];300                auto offset_next = c->stop_offsets[i + 1];301                assert(offset_next > offset_curr);302                if (t >= offset_curr && t < offset_next) {303                    auto color_curr = Vector4f{304                        c->stop_colors[4 * i + 0],305                        c->stop_colors[4 * i + 1],306                        c->stop_colors[4 * i + 2],307                        c->stop_colors[4 * i + 3]};308                    auto color_next = Vector4f{309                        c->stop_colors[4 * (i + 1) + 0],310                        c->stop_colors[4 * (i + 1) + 1],311                        c->stop_colors[4 * (i + 1) + 2],312                        c->stop_colors[4 * (i + 1) + 3]};313                    auto tt = (t - offset_curr) / (offset_next - offset_curr);314                    assert(isfinite(tt));315                    assert(isfinite(color_curr));316                    assert(isfinite(color_next));317                    return color_curr * (1 - tt) + color_next * tt;318                }319            }320            return Vector4f{c->stop_colors[4 * (c->num_stops - 1) + 0],321                            c->stop_colors[4 * (c->num_stops - 1) + 1],322                            c->stop_colors[4 * (c->num_stops - 1) + 2],323                            c->stop_colors[4 * (c->num_stops - 1) + 3]};324        } case ColorType::RadialGradient: {325            auto c = (const RadialGradient*)color;326            // Distance from pt to center327            auto offset = pt - c->center;328            auto normalized_offset = offset / c->radius;329            auto t = length(normalized_offset);330            // Find the correponding stop:331            if (t < c->stop_offsets[0]) {332                return Vector4f{c->stop_colors[0],333                                c->stop_colors[1],334                                c->stop_colors[2],335                                c->stop_colors[3]};336            }337            for (int i = 0; i < c->num_stops - 1; i++) {338                auto offset_curr = c->stop_offsets[i];339                auto offset_next = c->stop_offsets[i + 1];340                assert(offset_next > offset_curr);341                if (t >= offset_curr && t < offset_next) {342                    auto color_curr = Vector4f{343                        c->stop_colors[4 * i + 0],344                        c->stop_colors[4 * i + 1],345                        c->stop_colors[4 * i + 2],346                        c->stop_colors[4 * i + 3]};347                    auto color_next = Vector4f{348                        c->stop_colors[4 * (i + 1) + 0],349                        c->stop_colors[4 * (i + 1) + 1],350                        c->stop_colors[4 * (i + 1) + 2],351                        c->stop_colors[4 * (i + 1) + 3]};352                    auto tt = (t - offset_curr) / (offset_next - offset_curr);353                    assert(isfinite(tt));354                    assert(isfinite(color_curr));355                    assert(isfinite(color_next));356                    return color_curr * (1 - tt) + color_next * tt;357                }358            }359            return Vector4f{c->stop_colors[4 * (c->num_stops - 1) + 0],360                            c->stop_colors[4 * (c->num_stops - 1) + 1],361                            c->stop_colors[4 * (c->num_stops - 1) + 2],362                            c->stop_colors[4 * (c->num_stops - 1) + 3]};363        } default: {364            assert(false);365        }366    }367    return Vector4f{};368}369 370DEVICE371void d_sample_color(const ColorType &color_type,372                    void *color_ptr,373                    const Vector2f &pt,374                    const Vector4f &d_color,375                    void *d_color_ptr,376                    float *d_translation) {377    switch (color_type) {378        case ColorType::Constant: {379            auto d_c = (Constant*)d_color_ptr;380            atomic_add(&d_c->color[0], d_color);381            return;382        } case ColorType::LinearGradient: {383            auto c = (const LinearGradient*)color_ptr;384            auto d_c = (LinearGradient*)d_color_ptr;385            // Project pt to (c->begin, c->end)386            auto beg = c->begin;387            auto end = c->end;388            auto t = dot(pt - beg, end - beg) / max(dot(end - beg, end - beg), 1e-3f);389            // Find the correponding stop:390            if (t < c->stop_offsets[0]) {391                atomic_add(&d_c->stop_colors[0], d_color);392                return;393            }394            for (int i = 0; i < c->num_stops - 1; i++) {395                auto offset_curr = c->stop_offsets[i];396                auto offset_next = c->stop_offsets[i + 1];397                assert(offset_next > offset_curr);398                if (t >= offset_curr && t < offset_next) {399                    auto color_curr = Vector4f{400                        c->stop_colors[4 * i + 0],401                        c->stop_colors[4 * i + 1],402                        c->stop_colors[4 * i + 2],403                        c->stop_colors[4 * i + 3]};404                    auto color_next = Vector4f{405                        c->stop_colors[4 * (i + 1) + 0],406                        c->stop_colors[4 * (i + 1) + 1],407                        c->stop_colors[4 * (i + 1) + 2],408                        c->stop_colors[4 * (i + 1) + 3]};409                    auto tt = (t - offset_curr) / (offset_next - offset_curr);410                    // return color_curr * (1 - tt) + color_next * tt;411                    auto d_color_curr = d_color * (1 - tt);412                    auto d_color_next = d_color * tt;413                    auto d_tt = sum(d_color * (color_next - color_curr));414                    auto d_offset_next = -d_tt * tt / (offset_next - offset_curr);415                    auto d_offset_curr = d_tt * ((tt - 1.f) / (offset_next - offset_curr));416                    auto d_t = d_tt / (offset_next - offset_curr);417                    assert(isfinite(d_tt));418                    atomic_add(&d_c->stop_colors[4 * i], d_color_curr);419                    atomic_add(&d_c->stop_colors[4 * (i + 1)], d_color_next);420                    atomic_add(&d_c->stop_offsets[i], d_offset_curr);421                    atomic_add(&d_c->stop_offsets[i + 1], d_offset_next);422                    // auto t = dot(pt - beg, end - beg) / max(dot(end - beg, end - beg), 1e-6f);423                    // l = max(dot(end - beg, end - beg), 1e-3f)424                    // t = dot(pt - beg, end - beg) / l;425                    auto l = max(dot(end - beg, end - beg), 1e-3f);426                    auto d_beg = d_t * (-(pt - beg)-(end - beg)) / l;427                    auto d_end = d_t * (pt - beg) / l;428                    auto d_l = -d_t * t / l;429                    if (dot(end - beg, end - beg) > 1e-3f) {430                        d_beg += 2 * d_l * (beg - end);431                        d_end += 2 * d_l * (end - beg);432                    }433                    atomic_add(&d_c->begin[0], d_beg);434                    atomic_add(&d_c->end[0], d_end);435                    if (d_translation != nullptr) {436                        atomic_add(d_translation, (d_beg + d_end));437                    }438                    return;439                }440            }441            atomic_add(&d_c->stop_colors[4 * (c->num_stops - 1)], d_color);442            return;443        } case ColorType::RadialGradient: {444            auto c = (const RadialGradient*)color_ptr;445            auto d_c = (RadialGradient*)d_color_ptr;446            // Distance from pt to center447            auto offset = pt - c->center;448            auto normalized_offset = offset / c->radius;449            auto t = length(normalized_offset);450            // Find the correponding stop:451            if (t < c->stop_offsets[0]) {452                atomic_add(&d_c->stop_colors[0], d_color);453                return;454            }455            for (int i = 0; i < c->num_stops - 1; i++) {456                auto offset_curr = c->stop_offsets[i];457                auto offset_next = c->stop_offsets[i + 1];458                assert(offset_next > offset_curr);459                if (t >= offset_curr && t < offset_next) {460                    auto color_curr = Vector4f{461                        c->stop_colors[4 * i + 0],462                        c->stop_colors[4 * i + 1],463                        c->stop_colors[4 * i + 2],464                        c->stop_colors[4 * i + 3]};465                    auto color_next = Vector4f{466                        c->stop_colors[4 * (i + 1) + 0],467                        c->stop_colors[4 * (i + 1) + 1],468                        c->stop_colors[4 * (i + 1) + 2],469                        c->stop_colors[4 * (i + 1) + 3]};470                    auto tt = (t - offset_curr) / (offset_next - offset_curr);471                    assert(isfinite(tt));472                    // return color_curr * (1 - tt) + color_next * tt;473                    auto d_color_curr = d_color * (1 - tt);474                    auto d_color_next = d_color * tt;475                    auto d_tt = sum(d_color * (color_next - color_curr));476                    auto d_offset_next = -d_tt * tt / (offset_next - offset_curr);477                    auto d_offset_curr = d_tt * ((tt - 1.f) / (offset_next - offset_curr));478                    auto d_t = d_tt / (offset_next - offset_curr);479                    assert(isfinite(d_t));480                    atomic_add(&d_c->stop_colors[4 * i], d_color_curr);481                    atomic_add(&d_c->stop_colors[4 * (i + 1)], d_color_next);482                    atomic_add(&d_c->stop_offsets[i], d_offset_curr);483                    atomic_add(&d_c->stop_offsets[i + 1], d_offset_next);484                    // offset = pt - c->center485                    // normalized_offset = offset / c->radius486                    // t = length(normalized_offset)487                    auto d_normalized_offset = d_length(normalized_offset, d_t);488                    auto d_offset = d_normalized_offset / c->radius;489                    auto d_radius = -d_normalized_offset * offset / (c->radius * c->radius);490                    auto d_center = -d_offset;491                    atomic_add(&d_c->center[0], d_center);492                    atomic_add(&d_c->radius[0], d_radius);493                    if (d_translation != nullptr) {494                        atomic_add(d_translation, d_center);495                    }496                }497            }498            atomic_add(&d_c->stop_colors[4 * (c->num_stops - 1)], d_color);499            return;500        } default: {501            assert(false);502        }503    }504}505 506struct Fragment {507    Vector3f color;508    float alpha;509    int group_id;510    bool is_stroke;511};512 513struct PrefilterFragment {514    Vector3f color;515    float alpha;516    int group_id;517    bool is_stroke;518    int shape_id;519    float distance;520    Vector2f closest_pt;521    ClosestPointPathInfo path_info;522    bool within_distance;523};524 525DEVICE526Vector4f sample_color(const SceneData &scene,527                      const Vector4f *background_color,528                      const Vector2f &screen_pt,529                      const Vector4f *d_color = nullptr,530                      EdgeQuery *edge_query = nullptr,531                      Vector4f *d_background_color = nullptr,532                      float *d_translation = nullptr) {533    if (edge_query != nullptr) {534        edge_query->hit = false;535    }536 537    // screen_pt is in screen space ([0, 1), [0, 1)),538    // need to transform to canvas space539    auto pt = screen_pt;540    pt.x *= scene.canvas_width;541    pt.y *= scene.canvas_height;542    constexpr auto max_hit_shapes = 256;543    constexpr auto max_bvh_stack_size = 64;544    Fragment fragments[max_hit_shapes];545    int bvh_stack[max_bvh_stack_size];546    auto stack_size = 0;547    auto num_fragments = 0;548    bvh_stack[stack_size++] = 2 * scene.num_shape_groups - 2;549    while (stack_size > 0) {550        const BVHNode &node = scene.bvh_nodes[bvh_stack[--stack_size]];551        if (node.child1 < 0) {552            // leaf553            auto group_id = node.child0;554            const ShapeGroup &shape_group = scene.shape_groups[group_id];555            if (shape_group.stroke_color != nullptr) {556                if (within_distance(scene, group_id, pt, edge_query)) {557                    auto color_alpha = sample_color(shape_group.stroke_color_type,558                                                    shape_group.stroke_color,559                                                    pt);560                    Fragment f;561                    f.color = Vector3f{color_alpha[0], color_alpha[1], color_alpha[2]};562                    f.alpha = color_alpha[3];563                    f.group_id = group_id;564                    f.is_stroke = true;565                    assert(num_fragments < max_hit_shapes);566                    fragments[num_fragments++] = f;567                }568            }569            if (shape_group.fill_color != nullptr) {570                if (is_inside(scene, group_id, pt, edge_query)) {571                    auto color_alpha = sample_color(shape_group.fill_color_type,572                                                    shape_group.fill_color,573                                                    pt);574                    Fragment f;575                    f.color = Vector3f{color_alpha[0], color_alpha[1], color_alpha[2]};576                    f.alpha = color_alpha[3];577                    f.group_id = group_id;578                    f.is_stroke = false;579                    assert(num_fragments < max_hit_shapes);580                    fragments[num_fragments++] = f;581                }582            }583        } else {584            assert(node.child0 >= 0 && node.child1 >= 0);585            const AABB &b0 = scene.bvh_nodes[node.child0].box;586            if (inside(b0, pt, scene.bvh_nodes[node.child0].max_radius)) {587                bvh_stack[stack_size++] = node.child0;588            }589            const AABB &b1 = scene.bvh_nodes[node.child1].box;590            if (inside(b1, pt, scene.bvh_nodes[node.child1].max_radius)) {591                bvh_stack[stack_size++] = node.child1;592            }593            assert(stack_size <= max_bvh_stack_size);594        }595    }596    if (num_fragments <= 0) {597        if (background_color != nullptr) {598            if (d_background_color != nullptr) {599                *d_background_color = *d_color;600            }601            return *background_color;602        }603        return Vector4f{0, 0, 0, 0};604    }605    // Sort the fragments from back to front (i.e. increasing order of group id)606    // https://github.com/frigaut/yorick-imutil/blob/master/insort.c#L37607    for (int i = 1; i < num_fragments; i++) {608        auto j = i;609        auto temp = fragments[j];610        while (j > 0 && fragments[j - 1].group_id > temp.group_id) {611            fragments[j] = fragments[j - 1];612            j--;613        }614        fragments[j] = temp;615    }616    // Blend the color617    Vector3f accum_color[max_hit_shapes];618    float accum_alpha[max_hit_shapes];619    // auto hit_opaque = false;620    auto first_alpha = 0.f;621    auto first_color = Vector3f{0, 0, 0};622    if (background_color != nullptr) {623        first_alpha = background_color->w;624        first_color = Vector3f{background_color->x,625                               background_color->y,626                               background_color->z};627    }628    for (int i = 0; i < num_fragments; i++) {629        const Fragment &fragment = fragments[i];630        auto new_color = fragment.color;631        auto new_alpha = fragment.alpha;632        auto prev_alpha = i > 0 ? accum_alpha[i - 1] : first_alpha;633        auto prev_color = i > 0 ? accum_color[i - 1] : first_color;634        if (edge_query != nullptr) {635            // Do we hit the target shape?636            if (new_alpha >= 1.f && edge_query->hit) {637                // A fully opaque shape in front of the target occludes it638                edge_query->hit = false;639            }640            if (edge_query->shape_group_id == fragment.group_id) {641                edge_query->hit = true;642            }643        }644        // prev_color is alpha premultiplied, don't need to multiply with645        // prev_alpha646        accum_color[i] = prev_color * (1 - new_alpha) + new_alpha * new_color;647        accum_alpha[i] = prev_alpha * (1 - new_alpha) + new_alpha;648    }649    auto final_color = accum_color[num_fragments - 1];650    auto final_alpha = accum_alpha[num_fragments - 1];651    if (final_alpha > 1e-6f) {652        final_color /= final_alpha;653    }654    assert(isfinite(final_color));655    assert(isfinite(final_alpha));656    if (d_color != nullptr) {657        // Backward pass658        auto d_final_color = Vector3f{(*d_color)[0], (*d_color)[1], (*d_color)[2]};659        auto d_final_alpha = (*d_color)[3];660        auto d_curr_color = d_final_color;661        auto d_curr_alpha = d_final_alpha;662        if (final_alpha > 1e-6f) {663            // final_color = curr_color / final_alpha664            d_curr_color = d_final_color / final_alpha;665            d_curr_alpha -= sum(d_final_color * final_color) / final_alpha;666        }667        assert(isfinite(*d_color));668        assert(isfinite(d_curr_color));669        assert(isfinite(d_curr_alpha));670        for (int i = num_fragments - 1; i >= 0; i--) {671            // color[n] = prev_color * (1 - new_alpha) + new_alpha * new_color;672            // alpha[n] = prev_alpha * (1 - new_alpha) + new_alpha;673            auto prev_alpha = i > 0 ? accum_alpha[i - 1] : first_alpha;674            auto prev_color = i > 0 ? accum_color[i - 1] : first_color;675            auto d_prev_alpha = d_curr_alpha * (1.f - fragments[i].alpha);676            auto d_alpha_i = d_curr_alpha * (1.f - prev_alpha);677            d_alpha_i += sum(d_curr_color * (fragments[i].color - prev_color));678            auto d_prev_color = d_curr_color * (1 - fragments[i].alpha);679            auto d_color_i = d_curr_color * fragments[i].alpha;680            auto group_id = fragments[i].group_id;681            if (fragments[i].is_stroke) {682                d_sample_color(scene.shape_groups[group_id].stroke_color_type,683                               scene.shape_groups[group_id].stroke_color,684                               pt,685                               Vector4f{d_color_i[0], d_color_i[1], d_color_i[2], d_alpha_i},686                               scene.d_shape_groups[group_id].stroke_color,687                               d_translation);688            } else {689                d_sample_color(scene.shape_groups[group_id].fill_color_type,690                               scene.shape_groups[group_id].fill_color,691                               pt,692                               Vector4f{d_color_i[0], d_color_i[1], d_color_i[2], d_alpha_i},693                               scene.d_shape_groups[group_id].fill_color,694                               d_translation);695            }696            d_curr_color = d_prev_color;697            d_curr_alpha = d_prev_alpha;698        }699        if (d_background_color != nullptr) {700            d_background_color->x += d_curr_color.x;701            d_background_color->y += d_curr_color.y;702            d_background_color->z += d_curr_color.z;703            d_background_color->w += d_curr_alpha;704        }705    }706    return Vector4f{final_color[0], final_color[1], final_color[2], final_alpha};707}708 709DEVICE710float sample_distance(const SceneData &scene,711                      const Vector2f &screen_pt,712                      float weight,713                      const float *d_dist = nullptr,714                      float *d_translation = nullptr) {715    // screen_pt is in screen space ([0, 1), [0, 1)),716    // need to transform to canvas space717    auto pt = screen_pt;718    pt.x *= scene.canvas_width;719    pt.y *= scene.canvas_height;720    // for each shape721    auto min_group_id = -1;722    auto min_distance = 0.f;723    auto min_shape_id = -1;724    auto closest_pt = Vector2f{0, 0};725    auto min_path_info = ClosestPointPathInfo{-1, -1, 0};726    for (int group_id = scene.num_shape_groups - 1; group_id >= 0; group_id--) {727        auto s = -1;728        auto p = Vector2f{0, 0};729        ClosestPointPathInfo local_path_info;730        auto d = infinity<float>();731        if (compute_distance(scene, group_id, pt, infinity<float>(), &s, &p, &local_path_info, &d)) {732            if (min_group_id == -1 || d < min_distance) {733                min_distance = d;734                min_group_id = group_id;735                min_shape_id = s;736                closest_pt = p;737                min_path_info = local_path_info;738            }739        }740    }741    if (min_group_id == -1) {742        return min_distance;743    }744    min_distance *= weight;745    auto inside = false;746    const ShapeGroup &shape_group = scene.shape_groups[min_group_id];747    if (shape_group.fill_color != nullptr) {748        inside = is_inside(scene,749                           min_group_id,750                           pt,751                           nullptr);752        if (inside) {753            min_distance = -min_distance;754        }755    }756    assert((min_group_id >= 0 && min_shape_id >= 0) || scene.num_shape_groups == 0);757    if (d_dist != nullptr) {758        auto d_abs_dist = inside ? -(*d_dist) : (*d_dist);759        const ShapeGroup &shape_group = scene.shape_groups[min_group_id];760        const Shape &shape = scene.shapes[min_shape_id];761        ShapeGroup &d_shape_group = scene.d_shape_groups[min_group_id];762        Shape &d_shape = scene.d_shapes[min_shape_id];763        d_compute_distance(shape_group.canvas_to_shape,764                           shape_group.shape_to_canvas,765                           shape,766                           pt,767                           closest_pt,768                           min_path_info,769                           d_abs_dist,770                           d_shape_group.shape_to_canvas,771                           d_shape,772                           d_translation);773    }774    return min_distance;775}776 777// Gather d_color from d_image inside the filter kernel, normalize by778// weight_image.779DEVICE780Vector4f gather_d_color(const Filter &filter,781                        const float *d_color_image,782                        const float *weight_image,783                        int width,784                        int height,785                        const Vector2f &pt) {786    auto x = int(pt.x);787    auto y = int(pt.y);788    auto radius = filter.radius;789    assert(radius > 0);790    auto ri = (int)ceil(radius);791    auto d_color = Vector4f{0, 0, 0, 0};792    for (int dy = -ri; dy <= ri; dy++) {793        for (int dx = -ri; dx <= ri; dx++) {794            auto xx = x + dx;795            auto yy = y + dy;796            if (xx >= 0 && xx < width && yy >= 0 && yy < height) {797                auto xc = xx + 0.5f;798                auto yc = yy + 0.5f;799                auto filter_weight =800                    compute_filter_weight(filter, xc - pt.x, yc - pt.y);801                // pixel = \sum weight * color / \sum weight802                auto weight_sum = weight_image[yy * width + xx];803                if (weight_sum > 0) {804                    d_color += (filter_weight / weight_sum) * Vector4f{805                        d_color_image[4 * (yy * width + xx) + 0],806                        d_color_image[4 * (yy * width + xx) + 1],807                        d_color_image[4 * (yy * width + xx) + 2],808                        d_color_image[4 * (yy * width + xx) + 3],809                    };810                }811            }812        }813    }814    return d_color;815}816 817DEVICE818float smoothstep(float d) {819    auto t = clamp((d + 1.f) / 2.f, 0.f, 1.f);820    return t * t * (3 - 2 * t);821}822 823DEVICE824float d_smoothstep(float d, float d_ret) {825    if (d < -1.f || d > 1.f) {826        return 0.f;827    }828    auto t = (d + 1.f) / 2.f;829    // ret = t * t * (3 - 2 * t)830    //     = 3 * t * t - 2 * t * t * t831    auto d_t = d_ret * (6 * t - 6 * t * t);832    return d_t / 2.f;833}834 835DEVICE836Vector4f sample_color_prefiltered(const SceneData &scene,837                                  const Vector4f *background_color,838                                  const Vector2f &screen_pt,839                                  const Vector4f *d_color = nullptr,840                                  Vector4f *d_background_color = nullptr,841                                  float *d_translation = nullptr) {842    // screen_pt is in screen space ([0, 1), [0, 1)),843    // need to transform to canvas space844    auto pt = screen_pt;845    pt.x *= scene.canvas_width;846    pt.y *= scene.canvas_height;847    constexpr auto max_hit_shapes = 64;848    constexpr auto max_bvh_stack_size = 64;849    PrefilterFragment fragments[max_hit_shapes];850    int bvh_stack[max_bvh_stack_size];851    auto stack_size = 0;852    auto num_fragments = 0;853    bvh_stack[stack_size++] = 2 * scene.num_shape_groups - 2;854    while (stack_size > 0) {855        const BVHNode &node = scene.bvh_nodes[bvh_stack[--stack_size]];856        if (node.child1 < 0) {857            // leaf858            auto group_id = node.child0;859            const ShapeGroup &shape_group = scene.shape_groups[group_id];860            if (shape_group.stroke_color != nullptr) {861                auto min_shape_id = -1;862                auto closest_pt = Vector2f{0, 0};863                auto local_path_info = ClosestPointPathInfo{-1, -1, 0};864                auto d = infinity<float>();865                compute_distance(scene, group_id, pt, infinity<float>(),866                                 &min_shape_id, &closest_pt, &local_path_info, &d);867                assert(min_shape_id != -1);868                const auto &shape = scene.shapes[min_shape_id];869                auto w = smoothstep(fabs(d) + shape.stroke_width) -870                         smoothstep(fabs(d) - shape.stroke_width);871                if (w > 0) {872                    auto color_alpha = sample_color(shape_group.stroke_color_type,873                                                    shape_group.stroke_color,874                                                    pt);875                    color_alpha[3] *= w;876 877                    PrefilterFragment f;878                    f.color = Vector3f{color_alpha[0], color_alpha[1], color_alpha[2]};879                    f.alpha = color_alpha[3];880                    f.group_id = group_id;881                    f.shape_id = min_shape_id;882                    f.distance = d;883                    f.closest_pt = closest_pt;884                    f.is_stroke = true;885                    f.path_info = local_path_info;886                    f.within_distance = true;887                    assert(num_fragments < max_hit_shapes);888                    fragments[num_fragments++] = f;889                }890            }891            if (shape_group.fill_color != nullptr) {892                auto min_shape_id = -1;893                auto closest_pt = Vector2f{0, 0};894                auto local_path_info = ClosestPointPathInfo{-1, -1, 0};895                auto d = infinity<float>();896                auto found = compute_distance(scene,897                                              group_id,898                                              pt,899                                              1.f,900                                              &min_shape_id,901                                              &closest_pt,902                                              &local_path_info,903                                              &d);904                auto inside = is_inside(scene, group_id, pt, nullptr);905                if (found || inside) {906                    if (!inside) {907                        d = -d;908                    }909                    auto w = smoothstep(d);910                    if (w > 0) {911                        auto color_alpha = sample_color(shape_group.fill_color_type,912                                                        shape_group.fill_color,913                                                        pt);914                        color_alpha[3] *= w;915 916                        PrefilterFragment f;917                        f.color = Vector3f{color_alpha[0], color_alpha[1], color_alpha[2]};918                        f.alpha = color_alpha[3];919                        f.group_id = group_id;920                        f.shape_id = min_shape_id;921                        f.distance = d;922                        f.closest_pt = closest_pt;923                        f.is_stroke = false;924                        f.path_info = local_path_info;925                        f.within_distance = found;926                        assert(num_fragments < max_hit_shapes);927                        fragments[num_fragments++] = f;928                    }929                }930            }931        } else {932            assert(node.child0 >= 0 && node.child1 >= 0);933            const AABB &b0 = scene.bvh_nodes[node.child0].box;934            if (inside(b0, pt, scene.bvh_nodes[node.child0].max_radius)) {935                bvh_stack[stack_size++] = node.child0;936            }937            const AABB &b1 = scene.bvh_nodes[node.child1].box;938            if (inside(b1, pt, scene.bvh_nodes[node.child1].max_radius)) {939                bvh_stack[stack_size++] = node.child1;940            }941            assert(stack_size <= max_bvh_stack_size);942        }943    }944    if (num_fragments <= 0) {945        if (background_color != nullptr) {946            if (d_background_color != nullptr) {947                *d_background_color = *d_color;948            }949            return *background_color;950        }951        return Vector4f{0, 0, 0, 0};952    }953    // Sort the fragments from back to front (i.e. increasing order of group id)954    // https://github.com/frigaut/yorick-imutil/blob/master/insort.c#L37955    for (int i = 1; i < num_fragments; i++) {956        auto j = i;957        auto temp = fragments[j];958        while (j > 0 && fragments[j - 1].group_id > temp.group_id) {959            fragments[j] = fragments[j - 1];960            j--;961        }962        fragments[j] = temp;963    }964    // Blend the color965    Vector3f accum_color[max_hit_shapes];966    float accum_alpha[max_hit_shapes];967    auto first_alpha = 0.f;968    auto first_color = Vector3f{0, 0, 0};969    if (background_color != nullptr) {970        first_alpha = background_color->w;971        first_color = Vector3f{background_color->x,972                               background_color->y,973                               background_color->z};974    }975    for (int i = 0; i < num_fragments; i++) {976        const PrefilterFragment &fragment = fragments[i];977        auto new_color = fragment.color;978        auto new_alpha = fragment.alpha;979        auto prev_alpha = i > 0 ? accum_alpha[i - 1] : first_alpha;980        auto prev_color = i > 0 ? accum_color[i - 1] : first_color;981        // prev_color is alpha premultiplied, don't need to multiply with982        // prev_alpha983        accum_color[i] = prev_color * (1 - new_alpha) + new_alpha * new_color;984        accum_alpha[i] = prev_alpha * (1 - new_alpha) + new_alpha;985    }986    auto final_color = accum_color[num_fragments - 1];987    auto final_alpha = accum_alpha[num_fragments - 1];988    if (final_alpha > 1e-6f) {989        final_color /= final_alpha;990    }991    assert(isfinite(final_color));992    assert(isfinite(final_alpha));993    if (d_color != nullptr) {994        // Backward pass995        auto d_final_color = Vector3f{(*d_color)[0], (*d_color)[1], (*d_color)[2]};996        auto d_final_alpha = (*d_color)[3];997        auto d_curr_color = d_final_color;998        auto d_curr_alpha = d_final_alpha;999        if (final_alpha > 1e-6f) {1000            // final_color = curr_color / final_alpha1001            d_curr_color = d_final_color / final_alpha;1002            d_curr_alpha -= sum(d_final_color * final_color) / final_alpha;1003        }1004        assert(isfinite(*d_color));1005        assert(isfinite(d_curr_color));1006        assert(isfinite(d_curr_alpha));1007        for (int i = num_fragments - 1; i >= 0; i--) {1008            // color[n] = prev_color * (1 - new_alpha) + new_alpha * new_color;1009            // alpha[n] = prev_alpha * (1 - new_alpha) + new_alpha;1010            auto prev_alpha = i > 0 ? accum_alpha[i - 1] : first_alpha;1011            auto prev_color = i > 0 ? accum_color[i - 1] : first_color;1012            auto d_prev_alpha = d_curr_alpha * (1.f - fragments[i].alpha);1013            auto d_alpha_i = d_curr_alpha * (1.f - prev_alpha);1014            d_alpha_i += sum(d_curr_color * (fragments[i].color - prev_color));1015            auto d_prev_color = d_curr_color * (1 - fragments[i].alpha);1016            auto d_color_i = d_curr_color * fragments[i].alpha;1017            auto group_id = fragments[i].group_id;1018            if (fragments[i].is_stroke) {1019                const auto &shape = scene.shapes[fragments[i].shape_id];1020                auto d = fragments[i].distance;1021                auto abs_d_plus_width = fabs(d) + shape.stroke_width;1022                auto abs_d_minus_width = fabs(d) - shape.stroke_width;1023                auto w = smoothstep(abs_d_plus_width) -1024                         smoothstep(abs_d_minus_width);1025                if (w != 0) {1026                    auto d_w = w > 0 ? (fragments[i].alpha / w) * d_alpha_i : 0.f;1027                    d_alpha_i *= w;1028 1029                    // Backprop to color1030                    d_sample_color(scene.shape_groups[group_id].stroke_color_type,1031                                   scene.shape_groups[group_id].stroke_color,1032                                   pt,1033                                   Vector4f{d_color_i[0], d_color_i[1], d_color_i[2], d_alpha_i},1034                                   scene.d_shape_groups[group_id].stroke_color,1035                                   d_translation);1036 1037                    auto d_abs_d_plus_width = d_smoothstep(abs_d_plus_width, d_w);1038                    auto d_abs_d_minus_width = -d_smoothstep(abs_d_minus_width, d_w);1039 1040                    auto d_d = d_abs_d_plus_width + d_abs_d_minus_width;1041                    if (d < 0) {1042                        d_d = -d_d;1043                    }1044                    auto d_stroke_width = d_abs_d_plus_width - d_abs_d_minus_width;1045 1046                    const auto &shape_group = scene.shape_groups[group_id];1047                    ShapeGroup &d_shape_group = scene.d_shape_groups[group_id];1048                    Shape &d_shape = scene.d_shapes[fragments[i].shape_id];1049                    if (fabs(d_d) > 1e-10f) {1050                        d_compute_distance(shape_group.canvas_to_shape,1051                                           shape_group.shape_to_canvas,1052                                           shape,1053                                           pt,1054                                           fragments[i].closest_pt,1055                                           fragments[i].path_info,1056                                           d_d,1057                                           d_shape_group.shape_to_canvas,1058                                           d_shape,1059                                           d_translation);1060                    }1061                    atomic_add(&d_shape.stroke_width, d_stroke_width);1062                }1063            } else {1064                const auto &shape = scene.shapes[fragments[i].shape_id];1065                auto d = fragments[i].distance;1066                auto w = smoothstep(d);1067                if (w != 0) {1068                    // color_alpha[3] = color_alpha[3] * w;1069                    auto d_w = w > 0 ? (fragments[i].alpha / w) * d_alpha_i : 0.f;1070                    d_alpha_i *= w;1071 1072                    d_sample_color(scene.shape_groups[group_id].fill_color_type,1073                                   scene.shape_groups[group_id].fill_color,1074                                   pt,1075                                   Vector4f{d_color_i[0], d_color_i[1], d_color_i[2], d_alpha_i},1076                                   scene.d_shape_groups[group_id].fill_color,1077                                   d_translation);1078 1079                    // w = smoothstep(d)1080                    auto d_d = d_smoothstep(d, d_w);1081                    if (d < 0) {1082                        d_d = -d_d;1083                    }1084 1085                    const auto &shape_group = scene.shape_groups[group_id];1086                    ShapeGroup &d_shape_group = scene.d_shape_groups[group_id];1087                    Shape &d_shape = scene.d_shapes[fragments[i].shape_id];1088                    if (fabs(d_d) > 1e-10f && fragments[i].within_distance) {1089                        d_compute_distance(shape_group.canvas_to_shape,1090                                           shape_group.shape_to_canvas,1091                                           shape,1092                                           pt,1093                                           fragments[i].closest_pt,1094                                           fragments[i].path_info,1095                                           d_d,1096                                           d_shape_group.shape_to_canvas,1097                                           d_shape,1098                                           d_translation);1099                    }1100                }1101            }1102            d_curr_color = d_prev_color;1103            d_curr_alpha = d_prev_alpha;1104        }1105        if (d_background_color != nullptr) {1106            d_background_color->x += d_curr_color.x;1107            d_background_color->y += d_curr_color.y;1108            d_background_color->z += d_curr_color.z;1109            d_background_color->w += d_curr_alpha;1110        }1111    }1112    return Vector4f{final_color[0], final_color[1], final_color[2], final_alpha};1113}1114 1115struct weight_kernel {1116    DEVICE void operator()(int idx) {1117        auto rng_state = init_pcg32(idx, seed);1118        // height * width * num_samples_y * num_samples_x1119        auto sx = idx % num_samples_x;1120        auto sy = (idx / num_samples_x) % num_samples_y;1121        auto x = (idx / (num_samples_x * num_samples_y)) % width;1122        auto y = (idx / (num_samples_x * num_samples_y * width));1123        assert(y < height);1124        auto rx = next_pcg32_float(&rng_state);1125        auto ry = next_pcg32_float(&rng_state);1126        if (use_prefiltering) {1127            rx = ry = 0.5f;1128        }1129        auto pt = Vector2f{x + ((float)sx + rx) / num_samples_x,1130                           y + ((float)sy + ry) / num_samples_y};1131        auto radius = scene.filter->radius;1132        assert(radius >= 0);1133        auto ri = (int)ceil(radius);1134        for (int dy = -ri; dy <= ri; dy++) {1135            for (int dx = -ri; dx <= ri; dx++) {1136                auto xx = x + dx;1137                auto yy = y + dy;1138                if (xx >= 0 && xx < width && yy >= 0 && yy < height) {1139                    auto xc = xx + 0.5f;1140                    auto yc = yy + 0.5f;1141                    auto filter_weight = compute_filter_weight(*scene.filter,1142                                                               xc - pt.x,1143                                                               yc - pt.y);1144                    atomic_add(weight_image[yy * width + xx], filter_weight);1145                }1146            }1147        }1148    }1149 1150    SceneData scene;1151    float *weight_image;1152    int width;1153    int height;1154    int num_samples_x;1155    int num_samples_y;1156    uint64_t seed;1157    bool use_prefiltering;1158};1159 1160// We use a "mega kernel" for rendering1161struct render_kernel {1162    DEVICE void operator()(int idx) {1163        // height * width * num_samples_y * num_samples_x1164        auto pt = Vector2f{0, 0};1165        auto x = 0;1166        auto y = 0;1167        if (eval_positions == nullptr) {1168            auto rng_state = init_pcg32(idx, seed);1169            auto sx = idx % num_samples_x;1170            auto sy = (idx / num_samples_x) % num_samples_y;1171            x = (idx / (num_samples_x * num_samples_y)) % width;1172            y = (idx / (num_samples_x * num_samples_y * width));1173            assert(x < width && y < height);1174            auto rx = next_pcg32_float(&rng_state);1175            auto ry = next_pcg32_float(&rng_state);1176            if (use_prefiltering) {1177                rx = ry = 0.5f;1178            }1179            pt = Vector2f{x + ((float)sx + rx) / num_samples_x,1180                          y + ((float)sy + ry) / num_samples_y};1181        } else {1182            pt = Vector2f{eval_positions[2 * idx],1183                          eval_positions[2 * idx + 1]};1184            x = int(pt.x);1185            y = int(pt.y);1186        }1187 1188        // normalize pt to [0, 1]1189        auto npt = pt;1190        npt.x /= width;1191        npt.y /= height;1192        auto num_samples = num_samples_x * num_samples_y;1193        if (render_image != nullptr || d_render_image != nullptr) {1194            Vector4f d_color = Vector4f{0, 0, 0, 0};1195            if (d_render_image != nullptr) {1196                // Gather d_color from d_render_image inside the filter kernel1197                // normalize using weight_image1198                d_color = gather_d_color(*scene.filter,1199                                         d_render_image,1200                                         weight_image,

Showing the first 1,200 of 1793 lines. Download the file for the rest.