GithubHelp home page GithubHelp logo

cstatz / blazert Goto Github PK

View Code? Open in Web Editor NEW
11.0 2.0 4.0 12.32 MB

Double precision raytracer for scientific or engineering applications.

License: BSD 3-Clause "New" or "Revised" License

CMake 1.85% C++ 63.56% C 0.02% Jupyter Notebook 34.57%
raytracing raytracer engineering science bvh blaze double-precision cplusplus cpp17 scientific

blazert's Introduction

Build Status Documentation Status

blazeRT

  1. Introduction
    1. Contributing
    2. Versions
  2. Features
  3. Installation
    1. Dependencies
    2. Clone the repository
    3. Build and Test
    4. Configuration
  4. Usage
    1. Examples
    2. Minimal Examples
    3. Notes
  5. Benchmarks
  6. License

Introduction

blazeRT is a double precision ray tracer for scientific or engineering applications derived from nanoRT using blaze datatypes and written in modern C++17. blazeRTs scene interface is similar to embree and intents to be a minimal effort (nearly plugin-) replacement. blazeRT should work on any system and architecture for which a recent (C++17 compatible) compiler is available.

We aim at providing a simple and unambiguous high-level API for the ray-traversal. We do not aim at providing backwards-compatibility (especially to older C++ standards).

blazeRT makes use of the the blaze linear algebra library for its vector types. Because we rely on a well-tested and well-optimized linear algebra library (instead of using our own vector types), blazeRT can focus on the actual ray tracing algorithms. Furthermore, using types from a linear algebra library is advantageous for the subsequent development of scientific application where the vector types are needed again. blazeRT should work with any library providing these vector types as long as certain criteria are met (a minimal set of operation on these vector types).

blazeRT works with triangular meshes and simple primitives, but it is easy to extend blazeRT to work on polygons or more complex primitives. A template for user-defined geometries can be found here. If you implement new geometries, we are more than happy to receive a pull request from you to include it in blazeRT.

blazeRT is tested using unit tests (whose number will increase as development progresses) as well as by comparison of rendering results to reference images. Currently the unit tests cover roughly 90% of files and 69% of lines, but in the tests we try to catch as many (fringe-) cases as possible. We try to ensure high code quality and a reproducible build experience via continuous integration. During the CI process we build the examples and the tests, which need to run successfully in order for the CI to pass. Currently, blazeRT is CI-tested on Ubuntu 18.04 and macOS with gcc and clang.

image

(Rendered using the path_tracer example adapted from nanoRT and originally contributed by daseyb)

Contributing

We appreciate all contributions from issues to pull requests.

For contributing, please read the contribution guide.

Versions

The releases are based on the master branch. The release-version is tagged and follows the scheme Year.Quarter.Revision.

Features

  • modern C++
  • single and double precision ray tracing
  • simple and unambiguous high-level scene-based API
  • Embree fall back for single precision floats
  • using vector and matrix type from blaze for efficient linear algebra
  • currently supported geometry
    • triangular meshes
    • spheres
    • (finite) planes
    • cylinders
  • BVH accelerated ray racing
  • back-face culling (will be implemented)
  • unit tests via doctest
  • documentation of the test cases in test/doc
  • benchmark (comparing embree, nanoRT, madmann91/bvh and blazeRT) via google benchmark
  • cmake script to aggregate the code into a single-header library (not recommended)

Installation

Installation and build are tested on linux (e.g. ubuntu bionic, arch linux) and macOS. Before starting the build process please ensure all dependencies are properly installed and available to the project.

Dependencies

  • C++17 capable compiler
  • cmake (>= 3.11.0)
  • blaze (>= 3.7)
  • Embree (>= 3) if EMBREE_TRACING fallback is desired
  • doctest (for testing, pulled in as submodule)
  • google benchmark (for running the benchmarks)

Clone the repository

Clone the repository with the following command: git clone https://github.com/cstatz/blazert.git

For the tests and the benchmarks the submodules must be cloned as well:

git submodule init
git submodule update 

This will pull doctest, nanoRT and madmann91/bvh as submodules.

Build and test

For windows read this: building on windows

This is a header-only library. No need to build anything. Just drop it in your source directory and off you go. The build step is solely for the examples, tests and the benchmark.

We strictly recommend an out-of-source build in a separate directory (here for simplicity build) Starting in the source directory to project is build from the commandline as follows:

mkdir build
cd build 
ccmake ../  # create cache and configuration
cmake --build .
cmake --build . -- install  # If package needs to be installed 
ctest  # Runs the tests

For maximum performance, we recommend building with gcc which results in a 15% to 20% better performance compared to clang (on linux and macOS). The provided benchmarks might be used to tune the compilation flags for your specific system and architecture.

A word of caution: blazeRT will compile and work with compiler optimizations enabled (up to -O3), but needs infinite-math. If your application needs fast-math, ensure that the blazeRT code path is compiled with -fno-finite-math-only (in case of clang). In terms of performance, in its current form there is no major runtime difference between compilation with -O2 and -O3.

Configuration

The easiest way to set the configuration variables is by using ccmake or pass the variables via cmake ../ -D<VARIABLE>:<TYPE>=value.

  • ENABLE_OMP: Enable OpenMP in examples (for traversal)
  • BUILD_TEST: Build tests
  • BUILD_BENCHMARK: Build the benchmarks
  • BUILD_EXAMPLES: Build examples
  • EMBREE_BACKEND: Use Embree as single-precision floating point tracing backend
  • BLAZE_INCLUDE_OVERRIDE: Where to find the blaze includes. Must set on windows for blaze <= 3.7

Usage

To get familiar with the usage of blazeRT, look at the provided examples and test cases. To get started quickly, checkout the minimal examples below. The full documentation includeing the API reference is available here.

  • The scene API can be considered stable, while the low-level API of the BVH (build, traverse, ...) are not to be considered stable as they might change when requirements change with time.

  • To control the maximum tree depth set the BLAZERT_MAX_TREE_DEPTH to a sensible value before including blazert/blazert.h. Default value is 28 which corresponds to max. 134 million leafs in the BVH (for a well-balanced tree).

  • If single-precision floating-point ray tracing with Embree is desired define EMBREE_TRACING before including blazert/blazert.h. When tracing using the embree backend, vector types with an alignment of 16 bytes are needed (you have to ensure this yourself).

Either prim-references, -indices or primitives are stored in the bvh-nodes. Which case is implemented depends on the prim-collection.

Examples

Minimal Example

Example for using blazeRT can be found the the examples subdirectory.

To give you an impression of the High-Level API (adapted from examples/scene_primitives):

// snip 8<
  
  /// Create the scene and add some primitive collections:
  blazert::Scene<ft> scene;
  scene.add_cylinders(*centers, *semi_axes_a, *semi_axes_b, *heights, *rotations);
  ///scene.add_spheres( ... );
  ///scene.add_triangles( ... );
  
  /// Commit and build the scene
  scene.commit();
  
  /// Iterate over the ray directions:
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {

      const blazert::Ray<ft> ray{{0.0, 5.0, 20.0}, 
      		                    {static_cast<ft>((x / ft(width)) - 0.5),
                                 static_cast<ft>((y / ft(height)) - 0.5), 
                                 static_cast<ft>(-1.)}};
      blazert::RayHit<ft> rayhit;
      
      if (intersect1(scene, ray, rayhit)) { 
        /// Do something useful ...
      }
    }
  }

// >8 snip

Notes

  • Different from nanoRT, the bvh-build is not (yet) parallelized. For meshes with 5 million triangles, blazeRT needs about 5 seconds (on our tested systems) to build the BHV and about 20 seconds for 20 million triangles. For scientific or engineering ray tracing applications the scene is usually static while ray origin and direction are varied. The computational effort is hence defined mainly by the traversal and not the build. Ray traversal can be parallelized on application level (e.g. omp for loop when iterating over the ray (-directions), please have a look at the provided examples).
  • BLAZERTALIGN is currently unused but might be used in the future.

Benchmarks

We have included benchmarks comparing blazeRT to nanoRT, embree and madmann91/bvh for bvh build and traversal. The benchmark scene is a (triangle-) meshed sphere which which can be refined by sub-division. There are two traversal cases:

  • A rendering case where the scene size (number of triangles) is increased (while maintaining the same dimensions) and 8192 * 8192 rays are shot at the scene in a rectangular pattern. Here we expect log scaling with the number of triangles.
  • A scientific case where the scene size as well as the number of rays are increased (a ray is shot at each vertex of the sphere mesh). This can be considered as some kind of worst cast, because every ray hits and for each ray multiple overlapping bounding boxes (and subsequent primitives) need to be tested. Here we expect linear scaling with the number of triangles.

The benchmarks are run for the most recent (git-) revisions of the compared ray tracing libraries.

Please take the results with a grain of salt. The measured timings heavily depend on the chosen compiler, compiler version, level of optimization, operating system, system architecture, and the way the system is used otherwise (e.g. do multiple users have concurrent access to the system). We cannot guarantee that the optimal configuration (or even api) is chosen for all benchmarked libraries. Regarding embree: we're comparing traversal or intersection routines that are similar in behaviour. That means, for embree the benchmarks are performes with single ray traversal (rtcIntersect1). This is not optimal and embree is way more powerful (leveraging all the vectorization goodness) using the streaming api (or calls to rtcIntersectN).

The provided results were obtained using the following configuration:

  • OS: linux
  • Kernel: linux-5.6.15
  • CPU: Intel i5-8250U (8) @ 3.400GHz
  • RAM 32 GB
  • L1-Cache: 32 KB
  • L2-Cache: 256 KB
  • L3-Cache: 6144 KB
  • compiler: g++-10
  • optimization flags as documented in the projects root CMakeLists.txt
  • OpenMP disabled for build and traversal

The following plots show the benchmark results for the bvh build and the traversal for a realistic rendering case (not all rays hit) and for a realistic scientific rendering case (all rays hit).

Benchmark BVH build Benchmark BVH traversal rendering Benchmark BVH traversal scientific

License

blazeRT is licensed under the new BSD (3-clause) license. blazeRT is based on and inspired by nanoRT.h which is licensed under MIT-License. For comparability, nanoRTs path_tracer and madman91/bvhs benchmark have been adapted to blazeRT to generate a baseline for the tests and benchmarks.

We decided to not extend nanoRT due to the intrusive nature of the changes that came with the introduction of the blaze datatypes. Another reason was the possibility for an enhanced maintainability if the code is not kept (and developed) in a single-header library.

The examples are built around third-party libraries (e.g. tiny_obj_loader and stb_image_write) which adhere to their own respective licenses (found in the included files).

The rendering examples are taken from the nanoRT and bvh repos and serve as a baseline. The Lucy model included in the demo scene is taken from the Stanford 3D Scanning Repository: http://graphics.stanford.edu/data/3Dscanrep/

blazert's People

Contributors

cstatz avatar orgarten avatar tobiask-hfs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

blazert's Issues

[BUG] EmbreeScene can only add one primitive of one kind at the moment.

See blazert/embree/scene.h:

  1. add_spheres, add_planes, add_cylinders only adds the first object currently
  2. return geomID of this object
  3. all other objects in vectors will be ignores

This has to change in some way that we can return the geom_id in the same way as in for blazertscene, otherwise this is not really transparent.

[FEATURE] Enhance/Create API Documentation for the HL-API

Is your feature request related to a problem? Please describe.
Currently there is no or little documentation of the API.

Describe a possible solution.
At least a proper documentation of the High-Level-(Scene-)API should be created.
From the source-code documentation a tex, html or md document should be created.

Describe alternatives you've considered
Just let the developer figure this out.
Generate a API for all functions, classes, types.

[FEATURE] Provide vcpkg, conan, etc packages

Is your feature request related to a problem? Please describe.
Installing blazert could be much easier if packages would be provided.

Describe a possible solution.
Providing packages for various package managers would be beneficial for the user of the library:

  • vcpkg
  • conan
  • homebrew
  • AUR
  • .dev
  • .rpm
  • (windows installer)

Describe alternatives you've considered
Keep manually installing via cmd.

Additional context
Two AUR packages for blazert have been created for the main release branch as well as the develop branch:

[FEATURE] Count the number of intersections for a given range of primitives (prim_id and geom_id)

Is your feature request related to a problem? Please describe.
In order to test if you are inside or outside a given geometry or primitive (-collection), counting the number of intersections for a given range of primitives (by prim_id and geom_id) is necessary.

Describe a possible solution.
Provide an interface (scene api) that can count the number of intersections for a ray query (possibly a number of intersections traversal function (that can be give a geom_id and prim_id-range)).

Describe alternatives you've considered
Leave this up to the user.

Additional context
This function would be called infrequently (it is used to test whether or not a source or sink is inside a given object).

[FEATURE] additional primitive: plane

Is your feature request related to a problem? Please describe.
A plane primitive would be useful.

Describe a possible solution.
Planes are highly useful primitives for canonical examples or walls for indoor ray tracing.

Describe alternatives you've considered
A mesh could possibly do as well, but is much more computationally expensive.

[ENHANCEMENT] Refactor the tests to reduce lines of code in single file

Describe the enhancement
The structure for the test cases needs to be clearer. Some files contain 2k LOC which is way too much to keep an overview over whats tested and where to add new things.

Expected behavior
Primitives should get their own directory with multiple files named after what's being tested.

[FEATURE] Distance queries for the primitives (distance to surface)

Is your feature request related to a problem? Please describe.
Provide an interface to query the (closest-) distance to the surface from any point in space in or surrounding the scene, preferable using the already existing acceleration structures. This kind of query is useful in many applications involving inhomogeneous materials.

Describe a possible solution.
Implement a distance to surface function for each primitive. Implement a distance traversal for the bvh (check if point is inside bounding box of node).

Describe alternatives you've considered
Using a library that provides the acceleration structured and queries for the primitives.

Additional context
The query would be available in the scene api and provide the prim_id, geom_id, closest_point and closest_distance.

Test cases for blazeRT

We need test cases for blazeRT, at least with respect to the following functions

bvh/aabb.h:

  • intersect_node

bvh/accel.h:

  • intersect_leaf
  • traverse

bvh/bbox.h:

  • GetBoundingBox
  • compute_bounding_box

primitives/trimesh.h:

  • intersect
  • update_intersector (optional)
  • prepare_traversal (optional)
  • post_traversal (optional)

Feel free to add additional testing in case I missed any functions.

[FEATURE] Primitive: Box

Is your feature request related to a problem? Please describe.
New primitive: Box of arbitrary size -> can be constructed by 6 planes, but would be beneficial to have this kind of setup in order to speed up traversal. Furthermore, setting up a box via planes is cumbersome if done every time a box is needed.

Describe a possible solution.

  • Create 6 planes which construct the box
  • call intersect for such a contsruct

Describe alternatives you've considered
Setting up 6 individual planes, which is error prone.

**Needed: **

  • box blazeRT primitive

  • box as embree primitive

  • testing for blazeRT box

  • testing for embree box

  • support in blazertscene

  • support in EmbreeScene

  • testing of scene functions

Python bindings

Python bindings would be beneficial for use cases outside of EM. Could also be helpful for prototyping and debugging, e.g. prototyping Ray Material interaction, etc

[ENHANCEMENT] Rework the examples to reduce code duplication and warnings

Describe the enhancement
The examples should be reworked at some point in time to reduce code duplication and especially to make it easier to fix the warnings.

E.g. using ft = double and partially templated code in combination with float inputs for the geometry leads to some type conversion warnings which are tedious to fix.

Expected behavior
I think there are two options:

  1. remove templates and use float for everything
  2. templatize everything and use static_casts where appropriate.

The end goal would be to get rid of all warnings during the compilation of the code.

[ENHANCEMENT] Replace boolean variables in API by enum classes

Description
To increase the clarity in the API we should aim to remove all boolean variables in all function calls by appropriate enum classes in order to remove ambiguity. This will lead to less error prone usage of the code for all users. It also stated intent much clearer.

Currently, this is only the case for the constructor of Ray, which is easily fixed.

This issue is inspired by the points made in this talk.

[BUG] unambiguous id for each object

Describe the bug
For each hit, a prim_id is returned; however, prim_id is not unambiguous if more than one geometry type exists in the scene.

To reproduce

  1. Create a scene
  2. create 2 cylinders, and 2 sphere
  3. color each prim_id differently
    Result: two pairs of same-colored spheres and cylinders

Expected behavior
I would expect an unambiguous id in order to differentiate between all geometries, so in the context of blazeRT probably something along the lines of std::pair<unit32_t, uint32_t> for geom_id and prim_id which would allow for a correct mapping. There are probably better solutions than std::pair but the idea remains the same

[FEATURE] additional primitive: cylinder

Is your feature request related to a problem? Please describe.
A cylinder primitive would be useful.

Describe a possible solution.
Planes are highly useful primitives for canonical examples or columns in indoor ray tracing. Could also be beneficial for on-top em applications when considering simulations of pipes

Describe alternatives you've considered
A mesh could possibly do as well, but is much more computationally expensive.

[BUG]

Describe the bug
Usage with VisualStudio 2022 seems problematic

System
Which OS, compiler, and compiler version are you using:

  • OS: Windows10
  • Compiler and version:

Additional context
Additional documentation is needed, e.g. regarding the installation of the visual studio english language pack.

[FEATURE] Windows build and compilation README

Is your feature request related to a problem? Please describe.
A description of the build process on widows is missing.
Windows build differs from linux/macos. It is sometime not easy to get code to compile on windows that was primarily developed on linux or macos.

Describe a possible solution.
Write a README document specifically addressing the build process on windows.

[BUG] Only one primitive is rendered

Describe the bug
For sphere, cylinder and plate primitives only one object per BVH is traced apparently, no matter how many primitives are actually in the tree..

System
Which OS, compiler, and compiler version are you using:

  • OS: Arch Linux
  • Compiler and version: clang10, gcc10

To reproduce

#include <blazert/blazert.h>
#include <blazert/datatypes.h>
#include <iostream>
#include <memory>
#include <vector>

#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

using ft = double;

void SaveImagePNG(const char *filename, const float *rgb, int width,
                  int height) {
  auto *bytes = new unsigned char[width * height * 3];
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      const int index = y * width + x;
      bytes[index * 3 + 0] = (unsigned char) std::max(
          0.0f, std::min(rgb[index * 3 + 0] * 255.0f, 255.0f));
      bytes[index * 3 + 1] = (unsigned char) std::max(
          0.0f, std::min(rgb[index * 3 + 1] * 255.0f, 255.0f));
      bytes[index * 3 + 2] = (unsigned char) std::max(
          0.0f, std::min(rgb[index * 3 + 2] * 255.0f, 255.0f));
    }
  }
  stbi_write_png(filename, width, height, 3, bytes, width * 3);
  delete[] bytes;
}

int main(int argc, char **argv) {
  int width = 8192;
  int height = 8192;

  // the properties of the cylinders need to be saved on the heap
  auto centers = std::make_unique<blazert::Vec3rList<ft>>();
  auto semi_axes_a = std::make_unique<std::vector<ft>>();
  auto semi_axes_b = std::make_unique<std::vector<ft>>();
  auto heights = std::make_unique<std::vector<ft>>();
  auto rotations = std::make_unique<blazert::Mat3rList<ft>>();

  blazert::Mat3r<ft> rot{
      {0, 0, 1},
      {0, 1, 0},
      {-1, 0, 0}};

  /***
   * Each cylinder adds an element to the std::vectors containing the corresponding parameters
   */
  // cylinder 1 (this one is plotted)
  centers->emplace_back(blazert::Vec3r<ft>{-3, 3, 0});
  semi_axes_a->emplace_back(1);
  semi_axes_b->emplace_back(1);
  heights->emplace_back(1);
  rotations->push_back(rot);

  // cylinder 2 (this one is not plotted)
  centers->emplace_back(blazert::Vec3r<ft>{1, 4, 0});
  semi_axes_a->emplace_back(0.5);
  semi_axes_b->emplace_back(0.5);
  heights->emplace_back(2);
  rotations->push_back(rot);

  blazert::Scene<ft> scene;
  scene.add_cylinders(*centers, *semi_axes_a, *semi_axes_b, *heights, *rotations);
  scene.commit();

  // structure to save the colors
  std::vector<float> rgb(width * height * 3, 0.0f);

#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic, 1)
#endif
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {

      const blazert::Ray<ft> ray{{0.0, 5.0, 20.0}, {static_cast<ft>((x / ft(width)) - 0.5), static_cast<ft>((y / ft(height)) - 0.5), ft(-1.)}};
      blazert::RayHit<ft> rayhit;

      const bool hit = intersect1(scene, ray, rayhit);
      if (hit) {
          rgb[3 * ((height - y - 1) * width + x) + 0] = float(1);
          rgb[3 * ((height - y - 1) * width + x) + 1] = float(0);
          rgb[3 * ((height - y - 1) * width + x) + 2] = float(0);
      }
    }
  }

  SaveImagePNG("render.png", &rgb.at(0), width, height);

  return 0;
}

Expected behavior
I would expect two cylinders to be drawn, but only the first one shows up

Additional context
If I add a debug log to test_lead_nodes to print the num_primitives it only shows up as "1" all the time.

Tests for TriangleMesh

We need simple and verifiable test cases for the TriangleMesh<T> implemented in blazert/primitives/trimesh.h

Simliar to the tests for the other primitives, we need tests regarding the bounding box, centers and intersections for a TriangleMesh with only 1 triangle. (easiest case imaginable)

Second, for the intersections we need tests with more complicated triangle meshes (e.g. found in test_helpers.h.

Summary:

  • 1 triangle

    • bounding box
    • centers
    • intersections
  • real triangle mesh

    • intersections

Remove triangle precision tests from test_trimesh.cpp

Currently, precision tests for triangles and triangle meshes are done in test_trimesh.cpp. This isn't really the place for such tests since this is not really unit-testing.

We should refactor this and create an extra target which provides an executeable to be run on the target system which reports the precision.

[FEATURE] Generalize sphere into ellipsoid

Is your feature request related to a problem? Please describe.
Switching from a sphere primitive to an ellipsoid would greatly improve flexibility of the framework. Should be useful for scientific applications, because spherical bodies are usually only approximations.

Describe a possible solution.
An ellipsoid can be transformed to a sphere such that the existing intersection algorithms and so are still useful. The only thing that would need to be changed is a coordinate transformation before the intersection algorithm and the inverse transformation before setting the intersection points and normals.

Furhtermore, the test cases would need to be adapted due to an API change for the sphere. Alternatively, there could be a overload/type alias/... of some kind to provide a sphere with radius R in terms of an ellipsoid. C

Describe alternatives you've considered
Leave the Sphere primitive exactly the way it is and create a new primitive.

We should discuss which approach we are going to use.

The first approach would be API breaking, which in my opinion is ok at this stage of the project.
The second approach would not be API breaking but would already clutter the code base with unnecessary duplication or similar issues.

[FEATURE] Build examples in CI

Is your feature request related to a problem? Please describe.
The CI currently does not build the examples; however, it would be highly beneficial to verify that the examples still build even after changing stuff. This is especially relevant when changing API related things.

Describe a possible solution.
This change is rather simple. Just add the appropriate flags to the cmake script in the travis config.

Describe alternatives you've considered
Alternatively, we end up with plenty of potentially non-working examples for each PR.

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.