Git Product home page Git Product logo

nanort's Introduction

NanoRT, single header only modern ray tracing kernel.

Travis Build Status AppVeyor Build status

Path tracing example contributed by https://github.com/daseyb

NanoRT is simple single header only ray tracing kernel.

Features

  • Portable C++
  • BVH spatial data structure for efficient ray intersection finding.
    • Should be able to handle ~10M triangles scene efficiently with moderate memory consumption
  • Custom geometry & intersection
    • Built-in triangle mesh gemetry & intersector is provided.
  • Cross platform
    • MacOSX, Linux, Windows, iOS, Android, ARM, x86, SPARC, (maybe) MIPS, (will be) RISC-V, etc.
    • For example, NanoRT works finely on Raspberry Pi 2 (arm 32bit) and Raspberrry Pi 3!(AARCH64 kernel)
  • GPU efficient data structure
    • Built BVH tree from NanoRT is a linear array and does not have pointers, thus it is suited for GPU raytracing (GPU ray traversal).
  • OpenMP multithreaded BVH build.
  • Robust intersection calculation.
  • Double precision support
    • Beneficial for HPC and scientific visualization.

Applications

Projects using NanoRT

Projects similar/related to NanoRT

API

nanort::Ray represents ray. The origin org, the direction dir (not necessarily normalized), the minimum hit distance min_t(usually 0.0) and the maximum hit distance max_t (usually too far, e.g. 1.0e+30) must be filled before shooting ray.

nanort::BVHAccel builds BVH data structure from geometry, and provides the function to find intersection point for a given ray.

nanort::BVHBuildOptions specifies parameters for BVH build. Usually default parameters should work well.

nanort::BVHTraceOptions specifies ray traverse/intersection options.

template<typename T>
class {
  T org[3];        // [in] must set
  T dir[3];        // [in] must set
  T min_t;         // [in] must set
  T max_t;         // [in] must set
  unsigned int type;  // optional. ray type.
} Ray;

class BVHTraceOptions {
  // Trace rays only in face ids range. faceIdsRange[0] < faceIdsRange[1]
  // default: 0 to 0x3FFFFFFF(2G faces)
  unsigned int prim_ids_range[2];
  bool cull_back_face; // default: false
};

nanort::BVHBuildOptions<float> build_options; // BVH build option(optional)

const float *vertices = ...;
const unsigned int *faces = ...;

// Need to specify stride bytes for `vertices`.
// When vertex is stored XYZXYZXYZ... in float type, stride become 12(= sizeof(float) * 3).
nanort::TriangleMesh<float> triangle_mesh(vertices, faces, /* stride */sizeof(float) * 3);
nanort::TriangleSAHPred<float> triangle_pred(vertices, faces, /* stride */sizeof(float) * 3);

nanort::BVHAccel<float> accel;
ret = accel.Build(mesh.num_faces, triangle_mesh, triangle_pred, build_options);

nanort::TriangleIntersector<> triangle_intersecter(vertices, faces, /* stride */sizeof(float) * 3);

nanort::Ray<float> ray;
// fill ray org and ray dir.
...
// fill minimum and maximum hit distance.
ray.min_t = 0.0f;
ray.max_t = 1.0e+30f;

nanort::TriangleIntersection<float> isect;

// Store nearest hit point to `isect` and returns true if the hit point found.
BVHTraceOptions trace_options; // optional
bool hit = accel.Traverse(ray, triangle_intersecter, &isect, trace_options);

Application must prepare geometric information and store it in linear array.

For a builtin Triangle intersector,

  • vertices : The array of triangle vertices (e.g. xyz * numVertices)
  • faces : The array of triangle face indices (3 * numFaces)
  • stride : Byte stride of each vertex data

are required attributes.

Usage

// NanoRT defines template based class, so no NANORT_IMPLEMENTATION anymore.
#include "nanort.h"
Mesh mesh;
// load mesh data...
nanort::BVHBuildOptions<float> options; // Use default option
nanort::TriangleMesh<float> triangle_mesh(mesh.vertices, mesh.faces, /* stride */sizeof(float) * 3);
nanort::TriangleSAHPred<float> triangle_pred(mesh.vertices, mesh.faces, /* stride */sizeof(float) * 3);
nanort::BVHAccel<float> accel;
ret = accel.Build(mesh.vertices, mesh.faces, mesh.num_faces, options);
assert(ret);
nanort::BVHBuildStatistics stats = accel.GetStatistics();
printf("  BVH statistics:\n");
printf("    # of leaf   nodes: %d\n", stats.num_leaf_nodes);
printf("    # of branch nodes: %d\n", stats.num_branch_nodes);
printf("  Max tree depth   : %d\n", stats.max_tree_depth);

std::vector<float> rgb(width * height * 3, 0.0f);
const float tFar = 1.0e+30f;
// Shoot rays.
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int y = 0; y < height; y++) {
  for (int x = 0; x < width; x++) {
    BVHTraceOptions trace_options;
    // Simple camera. change eye pos and direction fit to .obj model.
    nanort::Ray<float> ray;
    ray.min_t = 0.0f;
    ray.max_t = tFar;
    ray.org[0] = 0.0f;
    ray.org[1] = 5.0f;
    ray.org[2] = 20.0f;
    float3 dir;
    dir[0] = (x / (float)width) - 0.5f;
    dir[1] = (y / (float)height) - 0.5f;
    dir[2] = -1.0f;
    dir.normalize();
    ray.dir[0] = dir[0];
    ray.dir[1] = dir[1];
    ray.dir[2] = dir[2];

    nanort::TriangleIntersector<> triangle_intersecter(mesh.vertices, mesh.faces, /* stride */sizeof(float) * 3);
    nanort::TriangleIntersection<> isect,
    bool hit = accel.Traverse(ray, triangle_intersector, &isect, trace_options);
    if (hit) {
      // Write your shader here.
      float3 normal;
      unsigned int fid = triangle_intersector.intersect.prim_id;
      normal[0] = mesh.facevarying_normals[3*3*fid+0]; // @todo { interpolate normal }
      normal[1] = mesh.facevarying_normals[3*3*fid+1];
      normal[2] = mesh.facevarying_normals[3*3*fid+2];
      // Flip Y
      rgb[3 * ((height - y - 1) * width + x) + 0] = fabsf(normal[0]);
      rgb[3 * ((height - y - 1) * width + x) + 1] = fabsf(normal[1]);
      rgb[3 * ((height - y - 1) * width + x) + 2] = fabsf(normal[2]);
    }
  }
}

Defines

NANORT_USE_CPP11_FEATURE : Enable C++11 feature
NANORT_ENABLE_PARALLEL_BUILD : Enable parallel BVH build(OpenMP version is not yet fully tested).

More example

See examples directory for example renderer using NanoRT.

Screenshots

Raytracing allows to implement different camera models quite easily. See examples/gui for different camera models which can be set via the config.json or the GUI itself:

  • perspective
  • orthographic
  • spherical FoV 120 & 180:
  • spherical-panorama FoV 120 & 180:
  • cylindrical: FoV 90 & 120:
  • fish-eye: FoV 120 & 180:
  • fish-eye MKX22: nonlinear fish-eye lens "iZugar MKX22 Fisheye Lens" with fixed FoV 220:

Custom geometry

Here is an example of custom geometry.

  • Spheres(particles) examples/particle_primitive/
  • Cubic Bezier Curves
    • Approximate as lines examples/curves_primitive/
    • Recursive Ray-Bezier intersection.
  • Cylinders examples/cylinder_primitive/

And plesae see API at wiki: https://github.com/lighttransport/nanort/wiki/API

License

nanort.h is licensed under MIT license.

NanoRT uses stack_container.h which is licensed under:

// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

NanoRT examples use some external third party libraries. Licenses for such third party libraries obey their own license.

Lucy statue model is from The Stanford 3D Scanning Repository http://graphics.stanford.edu/data/3Dscanrep/

TODO

PR are always welcome!

  • Optimize ray tracing kernel
  • Better cmake build.
  • Implement more Embree compatible API.
  • Scene graph support.
  • Fix multi-hit ray traversal.
  • Optimize Multi-hit ray traversal for BVH.
  • Ray traversal option.
    • FaceID range.
    • Double sided on/off.
    • Ray offset.
    • Avoid self-intersection(BVHTraceOptions.skip_prim_id).
    • Custom intersection filter through C++ template.
  • Fast BVH build
  • Efficient BVH build
    • Spatial split BVH
  • Motion blur
  • Fast, Accurate ray curve intersection
    • Phantom Ray-Hair Intersector
  • Example bi-directional path tracing renderer by @tatsy.

nanort's People

Contributors

daseyb avatar gjacquenot avatar jeffamstutz avatar kenichiice avatar koiava avatar lu-zero avatar nukebird avatar omigamedev avatar prabindh avatar schwieni avatar syoyo avatar szellmann avatar tabochans avatar takiyu avatar tatsy avatar tigrazone avatar urdh avatar weshoke avatar ybalrid avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nanort's Issues

why does binbuffer store each primitives' bounding boxes' minimum and maximum separately?

on 1114 th line,

There are 2 integers, left and right.
Inside the for loop, based on each cell's right side we increase the left and decrease the right by one.
The left integer will increase if the primitives' bounding box's minimum exists left to the cut position,
however, the right integer will decrease if the maximum of it exists left to the cut position.

If there happens to be a triangle existing across the cut position, which means the primitive's bounding box's minimum is located left to the cut position and the maximum of it is located right to the cut position.

Then the summation of the left and the right must not be same to the total number of primitives, since
this triangle belongs to the both left and right.

I think this is able to harm the right computation of SAH value and make it difficult to decide a good cut position although the building of child nodes is working well because the primitive's will be classified by its center.

Possible bug - intersector.Intersect(...) overrides uvs without ensuring local_t > 0

I'm reporting a possible bug related to the uv coordinates modified by the function

template <typename T = float, class H = TriangleIntersection<T>>
class TriangleIntersector {
...
bool Intersect(T *t_inout, unsigned int prim_index) { ... }    ln. 765
...
}

called by

inline bool BVHAccel<T>::TestLeafNode(const BVHNode<T> &node, const Ray<T> &ray,
 const I &intersector) const { ... }   		ln. 1823

After a careful review of my current project and after ensuring none of my code was interfering with texture sampling, I started to dig inside the ray-intersection routine and found a possible bug in those functions

In my current project, I'm using a textured light source whose emissive radiance is sampled from a texture, and in certain scenes the returned barycentric uvs for the emissive primitives is wrong, since apparently TestLeafNode lets the intersector change the uv values before ensuring the local_t is greater than 0 ( at ln.1848)

Here's a detailed explanation and an example of what is going wrong:

After the
bool BVHAccel<T>::Traverse ln.1936

function is called, the normal ray-intersection routine inside the while loop finds the correct leaf node at:

if (TestLeafNode(node, ray, intersector)) {
   hit_t = intersector.GetT();
}  // ln.1992

and stores the correct results for the primitive as e.g. t = 200.0, u = 0.88, v = 0.06

Since the while loop isn't yet complete, it keeps iterating the rest of the AABB stack until another Leaf gets hit and tested.
As soon as TestLeafNode( ... ) is called, two primitives needs to be tested. Both primitives will end up having a negative t value, since these objects are behind the ray direction, but here's the problem

ln. 1847
if (intersector.Intersect(&local_t, prim_idx)) {
   if (local_t > ray.min_t) {
        // Update isect state
       t = local_t;
       intersector.Update(t, prim_idx);
        
       hit = true;
   }
}

intersector.Intersect(...) will test those primitives and override the uv values previously stored u = 0.88 and v = 0.06 before ensuring the local_t of those primitives is greater than the ray.min_t

This way, I end up with the correct hit_t value (e.g. at 200.0) and incorrect uv values (the values of those primitives whose hit_t was negative)

Fixing the above issue completely solved the problem in my project

[TODO] Deprecate C++03

C++11 is now widely used(even in embedded devices and emerging architectures), so we are better to deprecate C++03.

Questions about differences between paper + NanoRT for ray-triangle intersection

I noticed that in the original paper, the following is done:

 /* Calculate scaled z-coordinates of vertices and use them to calculate hit
   * distance. */
  const float Az = Sz * A[kz];
  const float Bz = Sz * B[kz];
  const float Cz = Sz * C[kz];
  const float T = U * Az + V * Bz + W * Cz;

#ifdef BACKFACE_CULLING
  if (T < 0.0f || T > hit.t * det)
    return false;
#else
  int det_sign = sign_mask(det);
  if ((xorf(T, det_sign) < 0.0f) ||
      (xorf(T, det_sign) > (hit.t * xorf(det, det_sign))))
    return false;
#endif

  /* normalize U, V, W and T */
  hit.u = U * rcpDet;
  hit.v = V * rcpDet;
  hit.w = W * rcpDet;
  hit.t = T * rcpDet;

But in NanoRT, the following is done:

    const T Az = ray_coeff_.Sz * A[ray_coeff_.kz];
    const T Bz = ray_coeff_.Sz * B[ray_coeff_.kz];
    const T Cz = ray_coeff_.Sz * C[ray_coeff_.kz];
    const T D = U * Az + V * Bz + W * Cz;

    const T rcpDet = static_cast<T>(1.0) / det;
    T tt = D * rcpDet;

    if (tt > (*t_inout)) {
      return false;
    }

    if (tt < t_min_) {
      return false;
    }

    (*t_inout) = tt;
    // Use Möller-Trumbore style barycentric coordinates
    // U + V + W = 1.0 and interp(p) = U * p0 + V * p1 + W * p2
    // We want interp(p) = (1 - u - v) * p0 + u * v1 + v * p2;
    // => u = V, v = W.
    u_ = V * rcpDet;
    v_ = W * rcpDet;

    return true;

I am attempting to implement the algorithm from the paper myself, but am having some trouble understanding a few things.

1: Is multiplying by the inverse of the determinant somehow a way to convert from barycentric to Cartesian?

2: In the paper they have these "U, V, W" at the end. Is the idea that at the end of the algorithm, they leave the intersection point in barycentric form, rather than in Cartesian? If that's the case, what is happening at the end of NanoRT? What do u_ and v_ represent? And why is W not calculated?

3: If I want to know if I have an edge hit on a triangle, how do I tell?

Potential issue in examples/common/imgui/imgui_widgets.cpp: Unchecked return from initialization function

What is a Conditionally Uninitialized Variable? The return value of a function that is potentially used to initialize a local variable is not checked. Therefore, reading the local variable may result in undefined behavior.

1 instance of this defect were found in the following locations:


Instance 1
File : examples/common/imgui/imgui_widgets.cpp
Enclosing Function : SplitterBehavior@ImGui
Function : ButtonBehavior@ImGui

ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap);

Issue in: held, hovered

Code extract:

    bool hovered, held;
    ImRect bb_interact = bb;
    bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));
    ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); <------ HERE
    if (g.ActiveId != id)
        SetItemAllowOverlap();

How can I fix it?
Fix provided in corresponding Pull Request.

multi-hit ray traversal

I see that "Fix multi-hit ray traversal." is in the todo list for the project. What problem does it have; is it safe to use?

For now, in order to accomplish what I'm trying to do, I'm increasing min_t by a small amount and repeating Traverse. But it is slow and doesn't handle cases like two triangles coinciding at the same distance. What's the best way to implement this, at the moment? Thanks in advance for any help you are able to provide.

Potential issue in examples/common/imgui/imgui_widgets.cpp: Arithmetic Overflow in Expression

What is a Arithmetic Overflow? When a narrow type integral value was shifted left, multiplied, added, or subtracted and the result of that arithmetic operation was cast to a wider type value. If the operation overflowed the narrow type value, then data is lost. You can prevent this loss by converting the value to a wider type before the arithmetic operation.

2 instances of this defect were found in the following locations:


Instance 1
File : examples/common/imgui/imgui_widgets.cpp
Enclosing Function : $SliderBehaviorT@_J_JN@ImGui

FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t;

Issue in: v max, clicked t

Code extract:

                {
                    // For integer values we want the clicking position to match the grab box so we round above
                    // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property..
                    FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t; <------ HERE
                    TYPE v_new_off_floor = (TYPE)(v_new_off_f);
                    TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5);

How can I fix it?
Correct reference usage found in examples/common/imgui/imgui_widgets.cpp at line 1763.

v_cur = v_min + (TYPE)ImPow(ImSaturate((float)v_new_norm_curved), power) * (v_max - v_min);

Code extract:

        // Offset + round to user desired precision, with a curve on the v_min..v_max range to get more precision on one side of the range
        FLOATTYPE v_old_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power);
        FLOATTYPE v_new_norm_curved = v_old_norm_curved + (g.DragCurrentAccum / (v_max - v_min));
        v_cur = v_min + (TYPE)ImPow(ImSaturate((float)v_new_norm_curved), power) * (v_max - v_min); <------ HERE
        v_old_ref_for_accum_remainder = v_old_norm_curved;
    }

Instance 2
File : examples/common/imgui/imgui_widgets.cpp
Enclosing Function : $SliderBehaviorT@_K_JN@ImGui

FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t;

Issue in: v max, clicked t

Code extract:

                {
                    // For integer values we want the clicking position to match the grab box so we round above
                    // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property..
                    FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t; <------ HERE
                    TYPE v_new_off_floor = (TYPE)(v_new_off_f);
                    TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5);

How can I fix it?
Correct reference usage found in examples/common/imgui/imgui_widgets.cpp at line 1763.

v_cur = v_min + (TYPE)ImPow(ImSaturate((float)v_new_norm_curved), power) * (v_max - v_min);

Code extract:

        // Offset + round to user desired precision, with a curve on the v_min..v_max range to get more precision on one side of the range
        FLOATTYPE v_old_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power);
        FLOATTYPE v_new_norm_curved = v_old_norm_curved + (g.DragCurrentAccum / (v_max - v_min));
        v_cur = v_min + (TYPE)ImPow(ImSaturate((float)v_new_norm_curved), power) * (v_max - v_min); <------ HERE
        v_old_ref_for_accum_remainder = v_old_norm_curved;
    }

Remove inv_dir and dir_sign from Ray class or utilize them

Currently, the comments say they are filled internally. This is incorrect, these members are never either filled or read. Instead, Traverse(), MultiHitTraverse(), and ListNodeIntersections() all create these on the fly to pass to IntersectRayAABB().

Either they should be removed or used, having them there is confusing if they arent used.

[TODO] Deprecate OpenMP

C++11 is now widely used(even in HPC industries) and we are better to use C++11 thread instead of OpenMP threading.

cant use any function from imgui

main.cpp:(.text+0x7b): undefined reference to ImGui::DebugCheckVersionAndDataLayout(char const*, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int)'
collect2.exe: error: ld returned 1 exit status
Makefile:8: recipe for target 'output' failed
mingw32-make: *** [output] Error 1`

get this error when i try to compile with IMGUI_CHECKVERSION(); in my src
im using glfw if that matters

CMake build error relating to 'Dear ImGui'

I encountered this CMake build error relating to 'Dear ImGui' library on latest Ubuntu Linux:

[ 17%] Built target path_tracer
[ 23%] Linking CXX executable ../../bin/gui
[ 41%] Built target bidir_path_tracer
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGuiWindow::ImGuiWindow(ImGuiContext*, char const*)':
imgui.cpp:(.text+0x6a56): undefined reference to `ImGuiMenuColumns::ImGuiMenuColumns()'
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGui::ShowMetricsWindow(bool*)::Funcs::NodeDrawList(ImGuiWindow*, ImDrawList*, char const*) [clone .constprop.171]':
imgui.cpp:(.text+0x8b5b): undefined reference to `ImGui::TreeNode(void const*, char const*, ...)'
imgui.cpp:(.text+0x8c17): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x8cce): undefined reference to `ImGui::TreeNode(void const*, char const*, ...)'
imgui.cpp:(.text+0x8d81): undefined reference to `ImGui::TextColored(ImVec4 const&, char const*, ...)'
imgui.cpp:(.text+0x8d8b): undefined reference to `ImGui::TreePop()'
imgui.cpp:(.text+0x8f64): undefined reference to `ImGui::Selectable(char const*, bool, int, ImVec2 const&)'
imgui.cpp:(.text+0x8f99): undefined reference to `ImGui::TreePop()'
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGui::ShowMetricsWindow(bool*)::Funcs::NodeWindow(ImGuiWindow*, char const*)':
imgui.cpp:(.text+0x92f8): undefined reference to `ImGui::TreeNode(void const*, char const*, ...)'
imgui.cpp:(.text+0x9365): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x9407): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x9472): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x949c): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x94ca): undefined reference to `ImGui::BulletText(char const*, ...)'
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o:imgui.cpp:(.text+0x94ea): more undefined references to `ImGui::BulletText(char const*, ...)' follow
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGui::ShowMetricsWindow(bool*)::Funcs::NodeWindow(ImGuiWindow*, char const*)':
imgui.cpp:(.text+0x95d9): undefined reference to `ImGui::TreeNode(char const*, char const*, ...)'
imgui.cpp:(.text+0x9636): undefined reference to `ImGui::TreeNode(void const*, char const*, ...)'
imgui.cpp:(.text+0x9668): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x96b8): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x96c3): undefined reference to `ImGui::TreePop()'
imgui.cpp:(.text+0x96e4): undefined reference to `ImGui::TreeNode(char const*, char const*, ...)'
imgui.cpp:(.text+0x9733): undefined reference to `ImGui::TreePop()'
imgui.cpp:(.text+0x975d): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x97c1): undefined reference to `ImGui::TreePop()'
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGuiTextFilter::Draw(char const*, float)':
imgui.cpp:(.text+0xb1b7): undefined reference to `ImGui::InputText(char const*, char*, unsigned long, int, int (*)(ImGuiInputTextCallbackData*), void*)'
imgui.cpp:(.text+0xb200): undefined reference to `ImGui::InputText(char const*, char*, unsigned long, int, int (*)(ImGuiInputTextCallbackData*), void*)'
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGui::EndColumns()':
imgui.cpp:(.text+0x111a1): undefined reference to `ImGui::ButtonBehavior(ImRect const&, unsigned int, bool*, bool*, int)'
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGui::LogButtons()':
imgui.cpp:(.text+0x12fca): undefined reference to `ImGui::Button(char const*, ImVec2 const&)'
imgui.cpp:(.text+0x1304a): undefined reference to `ImGui::Button(char const*, ImVec2 const&)'
imgui.cpp:(.text+0x130ca): undefined reference to `ImGui::Button(char const*, ImVec2 const&)'
imgui.cpp:(.text+0x131a0): undefined reference to `ImGui::SliderInt(char const*, int*, int, int, char const*)'
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGui::Begin(char const*, bool*, int)':
imgui.cpp:(.text+0x15e58): undefined reference to `ImGuiMenuColumns::Update(int, float, bool)'
imgui.cpp:(.text+0x15f74): undefined reference to `ImGui::CloseButton(unsigned int, ImVec2 const&, float)'
imgui.cpp:(.text+0x1776e): undefined reference to `ImGui::ButtonBehavior(ImRect const&, unsigned int, bool*, bool*, int)'
imgui.cpp:(.text+0x178ec): undefined reference to `ImGui::ButtonBehavior(ImRect const&, unsigned int, bool*, bool*, int)'
imgui.cpp:(.text+0x188b8): undefined reference to `ImGui::CollapseButton(unsigned int, ImVec2 const&)'
imgui.cpp:(.text+0x188d3): undefined reference to `ImGui::Scrollbar(int)'
imgui.cpp:(.text+0x188e6): undefined reference to `ImGui::Scrollbar(int)'
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGui::SetTooltipV(char const*, __va_list_tag*)':
imgui.cpp:(.text+0x1d28f): undefined reference to `ImGui::TextV(char const*, __va_list_tag*)'
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGui::ShowMetricsWindow(bool*)':
imgui.cpp:(.text+0x1d87e): undefined reference to `ImGui::Text(char const*, ...)'
imgui.cpp:(.text+0x1d8ab): undefined reference to `ImGui::Text(char const*, ...)'
imgui.cpp:(.text+0x1d8d9): undefined reference to `ImGui::Text(char const*, ...)'
imgui.cpp:(.text+0x1d8f3): undefined reference to `ImGui::Text(char const*, ...)'
imgui.cpp:(.text+0x1d907): undefined reference to `ImGui::Text(char const*, ...)'
imgui.cpp:(.text+0x1d91a): undefined reference to `ImGui::Checkbox(char const*, bool*)'
imgui.cpp:(.text+0x1d92d): undefined reference to `ImGui::Checkbox(char const*, bool*)'
imgui.cpp:(.text+0x1d932): undefined reference to `ImGui::Separator()'
imgui.cpp:(.text+0x1d95c): undefined reference to `ImGui::TreeNode(char const*, char const*, ...)'
imgui.cpp:(.text+0x1d9d8): undefined reference to `ImGui::TreeNode(void const*, char const*, ...)'
imgui.cpp:(.text+0x1da3c): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x1dadf): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x1db52): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x1db88): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x1dbb9): undefined reference to `ImGui::BulletText(char const*, ...)'
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o:imgui.cpp:(.text+0x1dbdc): more undefined references to `ImGui::BulletText(char const*, ...)' follow
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGui::ShowMetricsWindow(bool*)':
imgui.cpp:(.text+0x1dc9b): undefined reference to `ImGui::TreePop()'
imgui.cpp:(.text+0x1dca5): undefined reference to `ImGui::TreePop()'
imgui.cpp:(.text+0x1dcc5): undefined reference to `ImGui::TreeNode(char const*, char const*, ...)'
imgui.cpp:(.text+0x1dd55): undefined reference to `ImGui::TreeNode(void const*, char const*, ...)'
imgui.cpp:(.text+0x1dde3): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x1de9c): undefined reference to `ImGui::TreeNode(void const*, char const*, ...)'
imgui.cpp:(.text+0x1df58): undefined reference to `ImGui::TextColored(ImVec4 const&, char const*, ...)'
imgui.cpp:(.text+0x1df61): undefined reference to `ImGui::TreePop()'
imgui.cpp:(.text+0x1df86): undefined reference to `ImGui::TreePop()'
imgui.cpp:(.text+0x1dfa6): undefined reference to `ImGui::TreeNode(char const*, char const*, ...)'
imgui.cpp:(.text+0x1e016): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x1e04f): undefined reference to `ImGui::TreePop()'
imgui.cpp:(.text+0x1e05b): undefined reference to `ImGui::TreeNode(char const*)'
imgui.cpp:(.text+0x1e0d7): undefined reference to `ImGui::Text(char const*, ...)'
imgui.cpp:(.text+0x1e100): undefined reference to `ImGui::Text(char const*, ...)'
imgui.cpp:(.text+0x1e135): undefined reference to `ImGui::Text(char const*, ...)'
imgui.cpp:(.text+0x1e174): undefined reference to `ImGui::Text(char const*, ...)'
imgui.cpp:(.text+0x1e198): undefined reference to `ImGui::Text(char const*, ...)'
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o:imgui.cpp:(.text+0x1e1c1): more undefined references to `ImGui::Text(char const*, ...)' follow
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGui::ShowMetricsWindow(bool*)':
imgui.cpp:(.text+0x1e2cf): undefined reference to `ImGui::TreePop()'
imgui.cpp:(.text+0x1e5f3): undefined reference to `ImGui::Selectable(char const*, bool, int, ImVec2 const&)'
imgui.cpp:(.text+0x1e62b): undefined reference to `ImGui::TreePop()'
imgui.cpp:(.text+0x1e8ca): undefined reference to `ImGui::TreeNode(char const*, char const*, ...)'
imgui.cpp:(.text+0x1e93b): undefined reference to `ImGui::TreeNode(void const*, char const*, ...)'
imgui.cpp:(.text+0x1e96d): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x1e9b8): undefined reference to `ImGui::BulletText(char const*, ...)'
imgui.cpp:(.text+0x1e9c3): undefined reference to `ImGui::TreePop()'
imgui.cpp:(.text+0x1e9e0): undefined reference to `ImGui::TreeNode(char const*, char const*, ...)'
imgui.cpp:(.text+0x1ea26): undefined reference to `ImGui::TreePop()'
imgui.cpp:(.text+0x1ea46): undefined reference to `ImGui::TreePop()'
imgui.cpp:(.text+0x1ea8e): undefined reference to `ImGui::BulletText(char const*, ...)'
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGui::EndFrame()':
imgui.cpp:(.text+0x1f136): undefined reference to `ImGui::Selectable(char const*, bool, int, ImVec2 const&)'
CMakeFiles/gui.dir/__/common/imgui/imgui.cpp.o: In function `ImGui::ShowMetricsWindow(bool*)::Funcs::NodeWindow(ImGuiWindow*, char const*)':
imgui.cpp:(.text+0x95ac): undefined reference to `ImGui::TreePop()'
CMakeFiles/gui.dir/main.cc.o: In function `main':
main.cc:(.text.startup+0x667): undefined reference to `ImGui::InputFloat3(char const*, float*, char const*, int)'
main.cc:(.text.startup+0x68c): undefined reference to `ImGui::InputFloat3(char const*, float*, char const*, int)'
main.cc:(.text.startup+0x6b1): undefined reference to `ImGui::InputFloat3(char const*, float*, char const*, int)'
main.cc:(.text.startup+0x6cf): undefined reference to `ImGui::RadioButton(char const*, int*, int)'
main.cc:(.text.startup+0x6f8): undefined reference to `ImGui::RadioButton(char const*, int*, int)'
main.cc:(.text.startup+0x721): undefined reference to `ImGui::RadioButton(char const*, int*, int)'
main.cc:(.text.startup+0x74a): undefined reference to `ImGui::RadioButton(char const*, int*, int)'
main.cc:(.text.startup+0x773): undefined reference to `ImGui::RadioButton(char const*, int*, int)'
CMakeFiles/gui.dir/main.cc.o:main.cc:(.text.startup+0x79c): more undefined references to `ImGui::RadioButton(char const*, int*, int)' follow
CMakeFiles/gui.dir/main.cc.o: In function `main':
main.cc:(.text.startup+0x7e9): undefined reference to `ImGui::InputFloat(char const*, float*, float, float, char const*, int)'
main.cc:(.text.startup+0x805): undefined reference to `ImGui::InputFloat2(char const*, float*, char const*, int)'
main.cc:(.text.startup+0x818): undefined reference to `ImGui::Checkbox(char const*, bool*)'
collect2: error: ld returned 1 exit status
examples/gui/CMakeFiles/gui.dir/build.make:334: recipe for target 'bin/gui' failed
make[2]: *** [bin/gui] Error 1
CMakeFiles/Makefile2:164: recipe for target 'examples/gui/CMakeFiles/gui.dir/all' failed
make[1]: *** [examples/gui/CMakeFiles/gui.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2

I fixed it by adding ../common/imgui/imgui_widgets.cpp to sources in examples/gui/CMakeLists.txt:

../common/imgui/imgui_draw.cpp

This might be typo.

1130th line on nanort.h

pos = bmin[j] + (i + 0.5f) * bstep[j];
should be
pos = bmin[j] + (i + 1.0f) * bstep[j];

There are some comments above this statement.

  //
  // Split pos bmin + (i + 1) * (bsize / BIN_SIZE)
  // +1 for i since we want a position on right side of the cell.
  //

This comment dosen't correspond to the below line.
I guess one of them is wrong.

Potential issue in examples/common/imgui/imgui.cpp: Unchecked return from initialization function

What is a Conditionally Uninitialized Variable? The return value of a function that is potentially used to initialize a local variable is not checked. Therefore, reading the local variable may result in undefined behavior.

2 instances of this defect were found in the following locations:


Instance 1
File : examples/common/imgui/imgui.cpp
Enclosing Function : UpdateManualResize@ImGui
Function : ButtonBehavior@ImGui

ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);

Issue in: held, hovered

Code extract:

        if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);
        if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);
        bool hovered, held;
        ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); <------ HERE
        if (hovered || held)
            g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;

How can I fix it?
Fix provided in corresponding Pull Request.


Instance 2
File : examples/common/imgui/imgui.cpp
Enclosing Function : UpdateManualResize@ImGui
Function : ButtonBehavior@ImGui

ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren);

Issue in: held, hovered

Code extract:

        const float BORDER_APPEAR_TIMER = 0.05f; // Reduce visual noise
        bool hovered, held;
        ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_size, BORDER_SIZE);
        ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren); <------ HERE
        if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || held)
        {

How can I fix it?
Fix provided in corresponding Pull Request.

Documentation Return type of Traverse

In the readme/documentation, the code example says that the accel.Traverse() returns the nearest hit point. However, it is a bool, so I don't think it can actually return a point.

// Returns nearest hit point(if exists)
BVHTraceOptions trace_options; // optional
bool hit = accel.Traverse(ray, triangle_intersecter, trace_options);

Is there a different method that does return the hitpoint?

[TODO] SIMD, BF16/FP16, INT8 optimization

Currently NanoRT does not utilize SIMD/AVX.

Also no quantized BVH support.

It'd be better to start to consider optimization and quantization.

Fortunately, recent CPU architecture(AlderLake, ZEN4) supports native BF16/FP16 and INT8 op support, which will boost quantized BVH construction/traversal.

Want to use nanort in GPU

Hi there, I'm very new here. I want to use nanort in CUDA, should I rewrite the data structure in CUDA way? Any help will be appreciated!

RayTracer with Color

Hello,
I am using the nanort for generating a depth map. I want to implement RGB color of the vertices in this too (in my function nanort::TriangleIntersector<>). Can I extract the color of vertices from nanort and add it to this function. Below is the code I have developed. Thanks.
`
void RTracer::construct_scene(const ShapeIO& shio,
const pcl::PointCloud<pcl_point_t>::Ptr cloud)
{
const pcl::PolygonMesh& pmesh = shio.mesh();

vertices.resize(cloud->size() * 3);
faces.resize(pmesh.polygons.size() * 3);

for (int i = 0; i < cloud->size(); ++i)
{
  vertices.at(i * 3 + 0) = cloud->at(i).x;
  vertices.at(i * 3 + 1) = cloud->at(i).y;
  vertices.at(i * 3 + 2) = cloud->at(i).z;
}

for (int i=0; i<pmesh.polygons.size(); ++i)
{
  faces.at(i * 3 + 0) = pmesh.polygons.at(i).vertices.at(0);
  faces.at(i * 3 + 1) = pmesh.polygons.at(i).vertices.at(1);
  faces.at(i * 3 + 2) = pmesh.polygons.at(i).vertices.at(2);
}

nanort::TriangleMesh<float> tmesh(
                                  vertices.data(),
                                  faces.data(),
                                  3 * sizeof(float));
nanort::TriangleSAHPred<float> tpred(
                                     vertices.data(),
                                     faces.data(),
                                     3 * sizeof(float));

nanort::BVHBuildOptions<float> opts;
opts.cache_bbox = false;

bvh.Build(pmesh.polygons.size(), tmesh, tpred, opts);

#if 0
nanort::BVHBuildStatistics stats = bvh.GetStatistics();
NUKLEI_LOG("Raytracer BVH statistics:\n" <<
"# of leaf nodes : " << stats.num_leaf_nodes << "\n" <<
"# of branch nodes : " << stats.num_branch_nodes << "\n" <<
"Max tree depth : " << stats.max_tree_depth);
#endif
}

//
// \brief Cast a ray and return the distance to mesh collision
// returns 0 in case of no collision
//
float RTracer::cast_ray(float viewpoint[3], float viewdir[3]) const
{
nanort::Ray ray;
// compiler converts float[3] in argument to float*
// casting float* to float[3] afterwards violates some dumb ISO rule
// workaround to disabling -fpermissive
std::memcpy(ray.org, viewpoint, sizeof(float) * 3);
std::memcpy(ray.dir, viewdir, sizeof(float) * 3);
ray.min_t = 0.0f;
ray.max_t = 2*RADIUS;
nanort::TriangleIntersector<> intersector(vertices.data(),
faces.data(),
3 * sizeof(float));
nanort::TriangleIntersection<> hit;
bool did_hit = bvh.Traverse(ray, intersector, &hit);
return did_hit ? hit.t : 0.0;
}`

Real time raycaster?

My first attempt was a creation of real-time raycaster (single hit, no recursive rays aka ray tracing)

For a blender monkey with ~15k tris I got ~100ms for FullHD frame. I tested every pixel on the screen (so 1920*1080 = 2073600 rays), model occupied 30-40% of window space

I knew that such a raycaster won't be fast, but to be honest I expected a bit more. Maybe I did something wrong? Or is here any tricks to achieve better performance? BVH and traversal settings are default, I'm testing everything in 1 thread

It's theoretical question, just wanna know how much speed I can get. Atm I'm using software rasterization (7-8ms for 1920x1080, 10-16ms at close range) and planning to use nanort for occlusion culling (so I'll stop rasterize triangles I can't see)

I'm sorry for such a silly question I just wanna know the limits. Here is always possibility that I'm doing something wrong :)

Fix call to Intersect() in TestLeafNodeIntersections()

The call here is different (I suspect old) in which the first two parameters are a min_t and max_t instead of local_t, so the call is invalid. Not sure what else needs to change.

If TestLeafNodeIntersections() itself is deprecated it should be removed or replaced.

MultiHitTraverse

Hi!

Is it safe to define the #if0 MultiHitTraverse? I need all hit triangles in the ray interval. I found an old issue related to this, which was closed.
#33
Best

Performance descriptions or wiki elaboration

I looked at the readme and wiki, but I don't think the performance is really covered that much besides some mentions of "efficient ray intersection finding". Would it be possible to elaborate on the performance characteristics of nanort? I found nanort from the issue on Embree not supporting double precision. One of the reasons I was starting with Embree was their paper on the high performance aspect of it, however for the scientific computing side the accuracy is also important.

Are there any benchmarks or even rough expectations for the difference between a single ray intersection with triangles in the BVH from nanort compared to Embree or the other raytracers?

Visible vertices computation

Hi,

I'm looking for a lightweight library to compute visible vertices of a 3D model given camera pose.
Sorry if my question may seem obvious, but I'm wondering if nanort is the right tool for that purpose, and if there are useful sample codes I can have a look at?

Thanks for your help,

Albert

Possible accuracy problem

I want to report a potential bug, with a minimal complete program to illustrate my findings.

I have tested nanort with doubles.
I have noticed that a slight modification (1e-16) on direction can lead nanort to miss intersection.
In this demo program, I have a single triangle with a single ray. Intersection is obvious and should be detected with float or double: we hit a point inside the triangle (not on edges). Other raytracer (embree, vtk) have found intersection with float and double.

@syoyo Could you please have a look at this program?

  • Called without argument, we run a normal case where a ray creates an intersection on a single triangle
  • Called with one argument, we slightty changes the direction and have a no intersection result, that is not normal

Here is the program:

// g++ -O0 -std=c++11 -Wall main_bug_1.cpp  -o main_bug_1
#include "nanort.h"
#include <iostream>

typedef double real;

int main(int argc, char * argv[])
{
    std::cout << "This program exposes a possible accuracy bug with nanort." << std::endl<<std::flush;
    std::cout << "A slight variation on direction[0] makes nanort misses a real intersection." << std::endl<<std::flush;
    std::cout << "Called without argument, we run a normal case where a ray creates an intersection on a single triangle" << std::endl<<std::flush;
    std::cout << "Called with one argument, we slightty changes the direction and have a no intersection result, that is not normal" << std::endl<<std::flush;
    bool activate_precision_bug = false;
    if (argc>1)
    {
        activate_precision_bug = true;
    }
    real vertices[9];
    unsigned int triangles[3] ={0,1,2};

    const real xMin=-1.0, xMax=+1.0;
    const real zMin=-3.0, zMax=+3.0;
    vertices[3 * 0] = xMax; vertices[3 * 0 + 1] = 2.0; vertices[3 * 0 + 2] = zMin;
    vertices[3 * 1] = xMin; vertices[3 * 1 + 1] = 2.0; vertices[3 * 1 + 2] = zMin;
    vertices[3 * 2] = xMax; vertices[3 * 2 + 1] = 2.0; vertices[3 * 2 + 2] = zMax;

    real origins[3];
    real directions[3];

    origins[0] = -0.36; origins[1] = +7.93890843; origins[2] = 1.2160368;
    directions[1] = -8.66025404e-01; directions[2] = -0.5;
    directions[0] = 0.0;
    if (activate_precision_bug)
    {
        directions[0] = -5.30287619e-17;
    }
    std::cout << "directions[0] = " << directions[0] << std::endl;

    nanort::BVHBuildOptions<real> build_options; // Use default option
    nanort::TriangleMesh<real> triangle_mesh(vertices, triangles, sizeof(real) * 3);
    nanort::TriangleSAHPred<real> triangle_pred(vertices, triangles, sizeof(real) * 3);
    nanort::BVHAccel<real> accel;
    build_options.cache_bbox = true;
    int ret = accel.Build((size_t) 1, triangle_mesh, triangle_pred, build_options);
    assert(ret);
    nanort::Ray<real> ray;
    nanort::TriangleIntersector<real, nanort::TriangleIntersection<real> > triangle_intersector(vertices, triangles, sizeof(real) * 3);
    nanort::TriangleIntersection<real> isect;

    ray.org[0] = origins[0];
    ray.org[1] = origins[1];
    ray.org[2] = origins[2];


    const real length = sqrt(directions[0] * directions[0] +
                             directions[1] * directions[1] +
                             directions[2] * directions[2]);
    ray.dir[0] = directions[0]/length;
    ray.dir[1] = directions[1]/length;
    ray.dir[2] = directions[2]/length;

    ray.min_t = 0.0;
    ray.max_t =  1.0e+30;

    const bool hit = accel.Traverse(ray, triangle_intersector, &isect);
    if (hit)
    {
        std::cout << "We have the expected result" << std::endl<<std::flush;
        std::cout << "Intersection isect.u =" << isect.u << " v = " << isect.v << std::endl<<std::flush;
    }
    else
    {
        std::cout << "No intersection detected" << std::endl<<std::flush;
        std::cout << "We get the wrong result" << std::endl<<std::flush;

    }
    return 0;
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.