Helm
by Paul West
HTML
<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three/build/three.module.js"
}
}
</script>
<script type="x-shader/x-vertex" id="vertexShader">
attribute vec3 center;
varying vec3 vCenter;
void main() {
vCenter = center;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script type="x-shader/x-fragment" id="fragmentShader">
uniform float thickness;
varying vec3 vCenter;
void main() {
vec3 afwidth = fwidth( vCenter.xyz );
vec3 edge3 = smoothstep( ( thickness - 1.0 ) * afwidth, thickness * afwidth, vCenter.xyz );
float edge = 1.0 - min( min( edge3.x, edge3.y ), edge3.z );
gl_FragColor.rgb = gl_FrontFacing ? vec3( 0.9, 0.9, 1.0 ) : vec3( 0.4, 0.4, 0.5 );
gl_FragColor.a = edge;
}
</script>
CSS
body {
margin: 0;
}
JavaScript
import * as THREE from 'three';
import {
GLTFLoader
} from 'https://unpkg.com/three/examples/jsm/loaders/GLTFLoader.js';
import {OrbitControls} from "https://unpkg.com/three/examples/jsm/controls/OrbitControls.js";
const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 100);
camera.position.z = 3;
const scene = new THREE.Scene();
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(animation);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera, renderer.domElement);
new GLTFLoader().load(
'https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf',
function(gltf) {
//console.log("gltf:", gltf);
//if (false)
{
gltf.scene.traverse(function(object) {
if (object.type === 'Mesh') {
let g = object.geometry.clone().toNonIndexed();
object.geometry.dispose();
object.geometry = g;
setupAttributes(object.geometry);
const material = new THREE.ShaderMaterial({
uniforms: {
'thickness': {
value: 1
}
},
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent,
side: THREE.DoubleSide,
alphaToCoverage: true // only works when WebGLRenderer's "antialias" is set to "true"
});
object.material = material;
}
});
}
scene.add(gltf.scene);
});
function animation(time) {
renderer.render(scene, camera);
}
function setupAttributes(geometry) {
const vectors = [
new THREE.Vector3(1, 0, 0),
new THREE.Vector3(0, 1, 0),
new THREE.Vector3(0, 0, 1)
];
const position = geometry.attributes.position;
const centers = new Float32Array(position.count * 3);
for (let i...