# Goal Create a "profile", i.e. a JSON file, to be used by the script that mass converts *.glb to *.png ## Introduction This exercise only requires that you launch a small HTTP server in a console. Otherwise, everything involved is handled through the HTML page lab.html. You will interact with lab.html's 3D rendering of a glb file that is included with this project. ## Steps Open a browser or a new window of the browser (Ctrl-n in Firefox) and resize the browser to a small rectangle. You are reducing the size so as to mimic what the PNG cell in the manifest table will look like. For example, this reduced window is 521 × 432 pixels. ![](20260304_101754_Wed.png) In a console: cd ~/work/Voron/voronstl/web python3 -m http.server 8001 You should have a console that looks like this: ![](20260304_101254_Wed.png) It is necessary to start the web server within the "web" directory as that directory will be servers "root". Visit: http://localhost:8001/lab.html You will see a zoomed-in image: ![](Screenshot_20260304_102153_AM_Wed-1.png) Zoom out until the entire part fits within the window. Click the Controls bar to collapse the sub menus. ![](Screenshot_20260304_102435_AM_Wed.png) Move the object to center it in the window: Shift + left mouse button. You want to have the entire part fit within the view and be cenetered. Click Controls bar to open the sub menus. Adjust the lighintensity to a high value, if not the maximum values. This will cause the image to go lighter allowing for contrast with shadows that help discern the part. ![](Screenshot_20260304_154311_PM_Wed.png) Optional: Save the PNG for your own reference. Click "Export Profile" and save your current settings. ![](20260304_102737_Wed.png) You now have a specification on sizing and angles which may work well for all of the other parts. Note: I took mine and applied the specifications saved above for a mass PNG creation and all the others looked very good. ### Additional Information: Here's what a JSON file looks like:
Image Description
jlpoole@jp ~/work/Voron/voronstl/web $ jq . out/three_profile_20260304_102657.json
{
  "provenance": "lab.html exportProfile Wed Mar 04 2026 10:26:57 GMT-0800 (Pacific Standard Time)",
  "output": {
    "width": 500,
    "height": 315,
    "pixelRatio": 1
  },
  "scene": {
    "background": 16777215
  },
  "camera": {
    "type": "PerspectiveCamera",
    "fov": 50,
    "near": 0.1,
    "far": 1000,
    "position": [
      11.93432933230491,
      11.71721921868296,
      13.265804443912849
    ],
    "up": [
      0,
      1,
      0
    ]
  },
  "controls": {
    "target": [
      2.075876663103527,
      -2.147814989328729,
      0.7122034073683343
    ]
  },
  "renderParams": {
    "wireframe": false,
    "edgeAngle": 30,
    "lightIntensity": 1.5
  },
  "lights": {
    "directional": {
      "position": [
        5,
        5,
        5
      ],
      "intensity": 1.5
    },
    "ambient": {
      "intensity": 0.6
    }
  }
}
jlpoole@jp ~/work/Voron/voronstl/web $ 

## ChatGPT's analysis of what is important for consistency Here is a response from ChatGPT that summarizes what variables are captured and saved so that they can be used to repeat the same type of transformation of *.glb to PNG. ## Response Good — now you’re at the point where it’s worth making the capture **repeatable** across a whole directory of GLBs. In practice, you want to “freeze” *everything that affects the pixels* and treat the GLB as the only variable. ## What to save (so every PNG is consistent) ### 1) Camera pose and projection Save: * `camera.position` (x,y,z) * `camera.up` (x,y,z) * `controls.target` (x,y,z) if you’re using OrbitControls * `camera.fov`, `camera.near`, `camera.far` * (optional) whether you used perspective vs orthographic These define “where the viewer is” and are the biggest determinant of repeatability. ### 2) Output framing rules (per-model normalization) You need a deterministic rule for “how big the part appears in frame”, otherwise parts with different sizes will look random. Two common strategies: **A. Fit-to-frame (recommended for mass renders)** For each GLB: * compute `Box3` bounds * compute `center` and `size` * set `controls.target = center` * set camera distance so the whole bounding box fits (using FOV + max dimension) * optionally lock azimuth/elevation (so every part is viewed from the same angle) This yields consistent “fill” and eliminates manual tuning. **B. Fixed camera (only if all parts are same scale and you want exact same viewpoint)** Works if your GLBs are all in the same coordinate system/scale and similarly sized. ### 3) Lighting rig Save: * light types (Directional/Hemisphere/Ambient) * each light’s `position`, `intensity`, `color` * shadow settings if used Your current screenshot shows a simple shaded render; keep lighting stable or you’ll get different highlight/shadow shapes across parts. ### 4) Renderer and capture settings Save: * `width`, `height` * `devicePixelRatio` you used (often set explicitly for determinism) * `renderer.toneMapping`, `outputColorSpace` (Three.js r152+ uses `renderer.outputColorSpace = THREE.SRGBColorSpace`) * `renderer.setClearColor()` (white) and alpha behavior * `preserveDrawingBuffer: true` (for browser capture) * whether you use `physicallyCorrectLights` (if you do, keep it constant) ### 5) Material overrides / wireframe settings If you’re overriding materials for “clay render” or wireframe, save: * your override material parameters (`MeshStandardMaterial` vs `MeshPhongMaterial`, roughness/metalness) * edge/wire settings (`EdgesGeometry` threshold angle, line material linewidth/color) ### 6) Background / staging Save: * background color (white) * whether you add a ground plane, axes, environment map, etc. Even “invisible” helpers can affect auto-framing if they’re included in bounds—so exclude them from bounds computation. --- ## The best thing to save: a small “render profile” JSON Put this in a file like `render_profile.json`: * canvas size * camera angle (azimuth/elevation) OR explicit camera position * fit-to-frame padding factor (e.g., 1.15) * light positions/intensities * material mode (`source`, `clay`, `wireframe`, `edges`) * background color Then your batch renderer (browser or headless) loads profile + GLB and produces PNG. Example shape (conceptually): ```json { "output": { "w": 1600, "h": 1200, "pixelRatio": 1 }, "background": "#ffffff", "camera": { "fov": 35, "near": 0.01, "far": 1000, "azimuth": -35, "elevation": 20, "fitPadding": 1.15 }, "lights": [ { "type": "dir", "pos": [3,5,2], "intensity": 2.8 }, { "type": "hem", "sky": "#ffffff", "ground": "#666666", "intensity": 0.6 } ], "materialMode": "clay" } ``` --- ## How to mass-create PNGs (two viable routes) ### Route 1: Headless batch (best) Use **node + headless WebGL** (or Puppeteer driving Chrome) to render each GLB with the profile and write PNGs. This is what you want if you’re doing hundreds/thousands. ### Route 2: Browser batch page (simpler) A page that: * loads a list of GLB URLs * applies the profile * renders each, downloads PNG automatically (or zips them) This is simpler but less pleasant at scale. --- ## One key question that determines the “right” profile Do you want your PNGs to be: * **same camera angle + fit-to-frame** (parts all fill the image similarly), or * **same absolute camera** (so relative size differences are visible)? If you tell me which you want, I’ll give you a concrete “profile JSON + camera fit math + batch driver” that matches it.