body {
  width: 100vw;
  height: 100vh;
  margin: 0;
  overflow: hidden;
  zoom: 1;
}
// Scene
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);

// Camera
const camera = new THREE.PerspectiveCamera(
  80,
  window.innerWidth / window.innerHeight,
  1,
  800
);
camera.position.set(0, 0, 2.5);

// Renderer
const renderer = new THREE.WebGLRenderer({ antiAlias: true });
renderer.setSize(window.innerWidth, window.innerHeight);

document.body.appendChild(renderer.domElement);

// Controls
const controls = new THREE.OrbitControls(camera);

controls.rotateSpeed = 0.3;
controls.zoomSpeed = 0.9;

controls.minDistance = 2.5;
controls.maxDistance = 20;

controls.minPolarAngle = 0; // radians
controls.maxPolarAngle = Math.PI;

controls.enableDamping = true;
controls.dampingFactor = 0.05;

// Lighting
const light = new THREE.AmbientLight(0x20202a, 15, 0);
light.position.set(30, 0, 30);
scene.add(light);

// Model Loader
const loader = new THREE.GLTFLoader();

loader.crossOrigin = true;

loader.load("https://jives.dev/lab/textures/wotlk/opposing_forces.glb", function (data) {
  const object = data.scene;
  object.position.set(0, 0, 0);
  scene.add(object);
});

// Lifecycle
function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
  renderer.setPixelRatio(window.devicePixelRatio);
  controls.update();
}

render();

window.addEventListener(
  "resize",
  function () {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  },
  false
);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.com/three@0.108.0/build/three.js
  2. https://unpkg.com/three@0.108.0/examples/js/controls/OrbitControls.js
  3. https://unpkg.com/three@0.108.0/examples/js/loaders/GLTFLoader.js