Rayrai Example: Aruco Marker

Overview

Uses an orthographic camera and flat lighting to render a textured mesh marker for detection-style pipelines. It is tuned for consistent marker appearance.

Binary

CMake target and executable name: rayrai_aruco_marker.

Run

Build and run from your build directory:

cmake --build . --target rayrai_aruco_marker
./rayrai_aruco_marker

On Windows, run rayrai_aruco_marker.exe instead. This example uses the in-process rayrai renderer (no external client required).

Details

  • Adds a mesh visual as a stand-in marker and rotates it to face the camera.

  • Configures a directional light and disables shadows for flat lighting.

  • Uses an orthographic camera to render a marker-like view.

Source

#include <memory>
#include <iostream>
#include <string>

#include <Eigen/Core>

#include "rayrai/example_common.hpp"
#include "rayrai/Camera.hpp"
#include "rayrai_example_resources.hpp"
#include "raisim/World.hpp"

int main(int argc, char* argv[]) {
  ExampleApp app;
  if (!app.init("rayrai_aruco_marker", 1024, 1024))
    return -1;

  auto world = std::make_shared<raisim::World>();
  world->setTimeStep(0.01);
  world->setGravity({0, 0, 0});

  auto viewer = std::make_shared<raisin::RayraiWindow>(world, 1024, 1024);
  // Use a slightly gray background so untextured meshes don't disappear on white.
  viewer->setBackgroundColor({235, 235, 235, 255});
  viewer->setFogDensity(0.0f);
  {
    // Shine light straight onto the marker plane (front-face points +Y).
    auto& light = viewer->getLight();
    light.setAsDirectional(glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f));
    // This is essentially a "viewer for a texture"; push ambient up so the marker reads clearly.
    light.ambient = glm::vec3(0.95f, 0.95f, 0.95f);
    light.diffuse = glm::vec3(1.00f, 1.00f, 1.00f);
    light.specular = glm::vec3(0.05f, 0.05f, 0.05f);
    // This example is a flat marker plane; shadows just make it darker.
    light.setShadowsEnabled(false);
  }

  const std::string markerMesh =
    rayraiRscPath(argv[0], "aruco_marker/aruco_marker.dae");
  auto marker = viewer->addVisualMesh("aruco_marker", markerMesh, 1.0, 1.0, 1.0);
  if (!marker) {
    std::cerr << "[rayrai_aruco_marker] Failed to load marker mesh: " << markerMesh << std::endl;
  } else {
    std::cerr << "[rayrai_aruco_marker] Loaded mesh: " << markerMesh
              << " (textures=" << (marker->hasTextures() ? "yes" : "no") << ")\n";
    marker->setPosition(0.0, 0.0, 1.0);
    // The Collada marker plane lies in XY with +Z normal. With the default camera here
    // (looking along -Y), the plane is edge-on. Rotate it so it faces the camera.
    // Rotation: -90 deg about +X -> normal +Z becomes +Y.
    marker->setOrientation(0.70710678, -0.70710678, 0.0, 0.0);  // w, x, y, z
    // Keep the texture un-tinted (the shader multiplies texture_diffuse by objectColor).
    marker->setColor(1.0, 1.0, 1.0, 1.0);
    marker->setDetectable(true);
  }

  // Add an axis indicator so it's obvious the scene is rendering even if the mesh/texture is missing.
  auto frame = viewer->addCoordinateFrame("origin");
  if (frame) {
    raisin::CoordinateFrame::Pose pose;
    pose.position = Eigen::Vector3d(0.0, 0.0, 1.0);
    pose.quaternion = Eigen::Vector4d(1.0, 0.0, 0.0, 0.0); // w,x,y,z
    frame->poses.push_back(pose);
    frame->frameSize = 0.25;
  }

  // Orthographic camera aimed at the marker plane.
  auto& cam = viewer->getCamera();
  cam.setProjectionMode(raisin::Camera::ProjectionMode::ORTHOGRAPHIC);
  cam.orthoScale = 1.0f;
  cam.position = glm::vec3(0.0f, 2.0f, 1.0f);
  cam.target = glm::vec3(0.0f, 0.0f, 1.0f);
  cam.yaw = -90.0f;
  cam.pitch = 0.0f;
  cam.update(false);

  while (!app.quit) {
    app.processEvents();
    if (app.quit)
      break;

    world->integrate();

    app.beginFrame();
    app.renderViewer(*viewer);
    app.endFrame();
  }

  viewer.reset();
  app.shutdown();
  return 0;
}