- camera movement
- sidewall uv
- photo by Nik
Last active
August 21, 2023 08:19
-
-
Save lostintangent/a52cbd3adff5a6b009319ce1e5693819 to your computer and use it in GitHub Desktop.
20.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as $ from 'https://unpkg.com/[email protected]/build/three.module.js' | |
import { OrbitControls } from 'https://unpkg.com/[email protected]/examples/jsm/controls/OrbitControls.js' | |
import { EffectComposer } from 'https://unpkg.com/[email protected]/examples/jsm/postprocessing/EffectComposer' | |
import { RenderPass } from 'https://unpkg.com/[email protected]/examples/jsm/postprocessing/RenderPass' | |
import { UnrealBloomPass } from 'https://unpkg.com/[email protected]/examples/jsm/postprocessing/UnrealBloomPass' | |
import { Curves } from 'https://unpkg.com/[email protected]/examples/jsm/curves/CurveExtras' | |
// ---- boot | |
const renderer = new $.WebGLRenderer({}); | |
const scene = new $.Scene(); | |
const camera = new $.PerspectiveCamera(75, 2, .01, 5000); | |
window.addEventListener('resize', () => { | |
const { clientWidth, clientHeight } = renderer.domElement; | |
renderer.setSize(clientWidth, clientHeight, false); | |
renderer.setPixelRatio(window.devicePixelRatio); | |
camera.aspect = clientWidth / clientHeight; | |
camera.updateProjectionMatrix(); | |
}); | |
document.body.prepend(renderer.domElement); | |
window.dispatchEvent(new Event('resize')); | |
// ---- setup | |
scene.fog = new $.FogExp2('black', 0.05); | |
scene.add(new $.HemisphereLight('cyan', 'orange', 2)); | |
// ---- const | |
const mpms = (20) / 1e3; | |
const steps = 2000; | |
// ---- mesh | |
const shape = new $.Shape(); | |
// cw | |
shape.moveTo(-5, -1); | |
shape.quadraticCurveTo(0, -4, 5, -1); | |
shape.lineTo(6, -1); | |
shape.quadraticCurveTo(0, -5, -6, -1); | |
const extrudePath = new Curves.TorusKnot(); | |
const UVGenerator = (() => { | |
let i = 0; // face id | |
return { | |
generateTopUV(...xs) { // for 2 "cap" faces | |
return [new $.Vector2(), new $.Vector2(), new $.Vector2()]; | |
}, | |
generateSideWallUV(_geom, _vs, _a, _b, _c, _d) { // all side faces | |
const segments = 5; // (shape-related; NOT eq `curveSegments`) | |
if (i < segments * steps) { // ignore bottom road faces | |
++i; | |
return [new $.Vector2(), new $.Vector2(), new $.Vector2(), new $.Vector2()]; | |
} | |
const n = i - segments * steps; // offseted face idx | |
const total_col_segments = 7; // (shape-related) | |
const col = n / steps | 0; | |
const left = col / total_col_segments; // normalize | |
const right = (col + 1) / total_col_segments; // normalize | |
const row = n % steps; | |
const bottom = row / steps; // normalize | |
const top = (row + 1) / steps; // normalize | |
++i; | |
return [ | |
new $.Vector2(left, bottom), // bottom left | |
new $.Vector2(right, bottom), // bottom right | |
new $.Vector2(right, top), // top right | |
new $.Vector2(left, top) // top left | |
]; | |
} | |
}; | |
})(); | |
const extrudeGeom = new $.ExtrudeBufferGeometry(shape, { | |
bevelEnabled: false, steps, extrudePath, | |
curveSegments: 5, UVGenerator | |
}); | |
const matSideWall = f(); | |
const matTop = new $.MeshLambertMaterial({ color: 'black' }); | |
const mesh = new $.Mesh(extrudeGeom, [matTop, matSideWall]); | |
scene.add(mesh); | |
// ---- composer | |
const composer = new EffectComposer(renderer); | |
composer.addPass(new RenderPass(scene, camera)); | |
composer.addPass(new UnrealBloomPass(new $.Vector2(), 2, 0.5, 0.7)); | |
// ---- anim | |
const totalLen = extrudePath.getLength(); | |
const { binormals } = extrudePath.computeFrenetFrames(steps); | |
const $m = new $.Matrix4(); // rotation matrix | |
renderer.setAnimationLoop((t /*ms*/) => { | |
const $u = ((mpms * t) % totalLen) / totalLen; | |
// update cam position | |
extrudePath.getPointAt($u, camera.position); | |
// update cam rotation | |
camera.setRotationFromMatrix($m.lookAt( | |
/* eye */ camera.position, | |
/* target */ extrudePath.getPointAt(Math.min(1.0, $u + 0.01)), | |
/* up */ binormals[$u * steps | 0] | |
)); | |
renderer.getDrawingBufferSize(composer.passes[1].resolution); | |
composer.render(); | |
mesh.material[1].uniforms.t.value = t; | |
}); | |
// ---- sidewall material | |
function f() { | |
const url = 'https://images.unsplash.com/photo-1550747528-cdb45925b3f7?ixid=MXwxMjA3fDB8MHxzZWFyY2h8Mnx8dW5pY29ybnxlbnwwfHwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=60'; | |
const tex = new $.TextureLoader().load(url); | |
tex.wrapS = $.MirroredRepeatWrapping; | |
tex.wrapT = $.RepeatWrapping; | |
const shader = $.ShaderLib.lambert; | |
const uniforms = $.UniformsUtils.merge([shader.uniforms, { | |
t: { value: 0 }, | |
stretch: { value: new $.Vector2(1, 10) }, | |
div: { value: new $.Vector2(32, 8) }, | |
}]); | |
const vertexShader = ` | |
uniform sampler2D map; | |
uniform vec2 stretch; | |
` + shader.vertexShader.replace('#include <uv_vertex>', ` | |
#ifdef USE_UV | |
vUv = ( uvTransform * vec3( uv, 1 ) ).xy * stretch; | |
#endif | |
`); | |
const fragmentShader = ` | |
uniform vec2 div; | |
uniform vec2 stretch; | |
uniform float t; | |
` + shader.fragmentShader.replace('#include <map_fragment>', ` | |
#ifdef USE_MAP | |
{ | |
vec2 i = vec2(ivec2( vUv * div )); | |
vec4 tA = texture2D( map, ( i ) / div ); | |
vec4 tB = texture2D( map, ( i + 1.0 ) / div ); | |
vec4 texel = 0.5 * (mix( tA, tB, vUv.x ) + mix( tA, tB, vUv.y )); | |
texel.b = step( 0.5, texel.b ) + ( sin( t * 0.001 ) * 0.5 + 0.5 ); | |
texel.r *= 2.0; | |
texel.g *= 0.5; | |
vec2 uv = fract( vUv * stretch * div ); | |
texel *= step( 0.2, uv.x ) * step ( 0.2, uv.y ); | |
diffuseColor *= texel; | |
} | |
#endif | |
`); | |
const mat = new $.ShaderMaterial({ | |
uniforms, vertexShader, fragmentShader, | |
lights: true, fog: true | |
}); | |
mat.map = mat.uniforms.map.value = tex; | |
return mat; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
canvas { | |
display: block; width:100%; height:100vh; | |
} | |
body { | |
margin: 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment