<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.169.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.169.0/examples/jsm/"
}
}
</script>
body{
overflow: hidden;
margin: 0;
}
// https://discourse.threejs.org/t/moving-road-effect-using-a-normal-map-similar-to-car-materials-example/72879/8
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
console.clear();
let scene = new THREE.Scene();
scene.background = new THREE.Color("#440000");
let camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 0.5, 1).setLength(10);
let renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(innerWidth, innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
window.addEventListener("resize", () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
});
let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
let light = new THREE.DirectionalLight(0xffffff, Math.PI);
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
light.shadow.intensity = Math.PI * 0.5;
light.position.setScalar(5);
scene.add(light, new THREE.AmbientLight(0xffffff, Math.PI * 0.5));
let road = new THREE.Mesh(
new THREE.PlaneGeometry(10, 10, 2, 2).rotateX(-Math.PI * 0.5),
new THREE.MeshLambertMaterial({
//fog: false,
map: new THREE.TextureLoader().load(
"https://threejs.org/examples/textures/hardwood2_diffuse.jpg",
(tex) => {
tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
tex.colorSpace = "srgb";
tex.anisotropy = renderer.capabilities.getMaxAnisotropy();
}
),
onBeforeCompile: (shader) => {
shader.uniforms.distMin = { value: 0.25 };
shader.uniforms.distMax = { value: 1 };
shader.uniforms.distCol = { value: scene.background };
//console.log(shader.vertexShader);
shader.fragmentShader = `
uniform float distMin;
uniform float distMax;
uniform vec3 distCol;
${shader.fragmentShader}
`.replace(
`#include <tonemapping_fragment>`,
`#include <tonemapping_fragment>
gl_FragColor.rgb = mix(gl_FragColor.rgb, distCol, smoothstep(distMin, distMax, length(vUv - 0.5) * 2.));
`
);
//console.log(shader.fragmentShader);
}
})
);
road.material.defines = { USE_UV: "" };
road.receiveShadow = true;
scene.add(road);
let shapeWheel = new THREE.Shape().absarc(0, 0, 1.5, 0, Math.PI * 2);
shapeWheel.holes = Array.from({ length: 6 }, (_, hIdx) => {
return new THREE.Path(
[
[1.3, 0.5],
[1.3, -0.5],
[0.3, -0.1],
[0.3, 0.1]
].map((p) => {
return new THREE.Vector2(...p).rotateAround(
new THREE.Vector2(),
(Math.PI * 2 / 6) * hIdx
);
})
);
});
let wheel = new THREE.Mesh(
new THREE.ExtrudeGeometry(shapeWheel, {
depth: 0.5,
curveSegments: 20,
bevelEnabled: false
}).center(),
new THREE.MeshLambertMaterial({
color: "brown",
map: new THREE.TextureLoader().load(
"https://threejs.org/examples/textures/hardwood2_diffuse.jpg",
(tex) => {
tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
tex.colorSpace = "srgb";
tex.anisotropy = renderer.capabilities.getMaxAnisotropy();
}
)
})
);
wheel.material.wrapT = wheel.material.wrapS = THREE.RepeatWrapping;
wheel.position.set(0, 1.5, 0);
wheel.castShadow = true;
scene.add(wheel);
let clock = new THREE.Clock();
let t = 0;
renderer.setAnimationLoop(() => {
let dt = clock.getDelta();
t += dt;
controls.update();
road.material.map.offset.x = t * 0.1;
// speed = 10 * 0.1 = 1 unit / sec
// angular speed = speed / radius = 1 / 1.5
wheel.rotation.z = -(1 / 1.5) * t;
renderer.render(scene, camera);
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.