Deep THREE.js rendering knowledge for the 3D panel: WebGL pipeline, buffer management, instanced rendering, shader considerations, and scene optimization techniques.
59
67%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./.github/skills/3d-rendering/SKILL.mdconst renderer = new THREE.WebGLRenderer({
canvas,
antialias: true,
alpha: true,
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.outputColorSpace = THREE.SRGBColorSpace;requestAnimationFramepackages/suite-base/src/panels/ThreeDeeRender/DynamicBufferGeometry.ts:
class DynamicBufferGeometry extends THREE.BufferGeometry {
// Grows to EXACTLY itemCount when capacity is exceeded — no geometric doubling.
resize(itemCount: number): void {
this.setDrawRange(0, itemCount);
if (itemCount <= this.#itemCapacity) {
return; // capacity sufficient — only the draw range changed
}
// For each attribute, allocate a NEW typed array of exactly itemCount * itemSize
// (old data is NOT copied; callers refill the buffer after resize)
this.#itemCapacity = itemCount;
}
}resize(itemCount) always calls setDrawRange(0, itemCount) firstitemCount <= itemCapacity, it returns early — buffers are reused, only the draw range movesitemCount > itemCapacity, each attribute is reallocated to exactly itemCount * itemSize
(no * 2 over-allocation, no copy of existing data)⚠️ Do not assume geometric/amortized doubling here. Repeatedly increasing the count by small increments reallocates every time, so callers that know a target size should resize to it once.
Raw message (PointCloud2)
│
▼
Decode fields (x, y, z, rgb, intensity)
│
▼
Fill position buffer (Float32Array)
Fill color buffer (Uint8Array)
│
▼
Upload to GPU (BufferAttribute.needsUpdate = true)
│
▼
Render with THREE.Points or InstancedMeshdecayTime in secondsdrawRange start forwardfilterQueue: processes messages in batches per frameworld (root)
├── base_link
│ ├── lidar_link
│ ├── camera_link
│ └── imu_link
└── map
└── odom
└── base_link (loop via static transform)// TransformTree.apply has an 8-argument signature:
const pose = transformTree.apply(
output, // Pose written in place (returned, or undefined on failure)
input, // Readonly<Pose> source pose
frameId, // destination/target frame
rootFrameId, // optional explicit root frame (defaults to frame.root())
srcFrameId, // source frame
dstTime, // Time to evaluate the destination frame at
srcTime, // Time to evaluate the source frame at
maxDelta, // optional Duration cap on extrapolation
);packages/suite-base/src/panels/ThreeDeeRender/transforms/TransformTree.tsoutput Pose and returns it (or undefined if a frame is missing)maxDelta caps extrapolation from stale dataFor many identical objects (markers, arrows):
const mesh = new THREE.InstancedMesh(geometry, material, maxCount);
// Update per-instance transform
mesh.setMatrixAt(index, matrix);
mesh.instanceMatrix.needsUpdate = true;maxCount determines GPU buffer allocation — avoid over-allocationTHREE.ShaderMaterial or THREE.RawShaderMaterialsizeAttenuation: true)vertexColors: true on materialDynamicBufferGeometry — never new BufferGeometry() per frameneedsUpdate = true only on changed attributesInstancedMesh for repeated geometries (>10 instances)THREE.Material per framerenderer.render() if scene hasn't changedtraverse() in hot path — cache node references0900ce3
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.