Three.js 莫比乌斯带

文化   2024-11-11 08:01   河南  

使用 Three.js 的 WebGL 小实验。Three.js 莫比乌斯带。

实现代码

HTML:

<canvas id="mobius"></canvas>

<script type="importmap">{"imports": {"three": "https://cdnjs.cloudflare.com/ajax/libs/three.js/0.151.0/three.module.min.js","three/addons/": "https://unpkg.com/three@0.151.0/examples/jsm/"}}</script><script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
CSS:
html, body {    padding: 0;    margin: 0;}

body { width: 100%; height: 100vh; display: flex; align-items: center; justify-content: center; background-color: rgb(255, 255, 242);}

#mobius { cursor: pointer; padding-top: 75px; /* for codepen preview */}

.lil-gui { --width: 450px; max-width: 90%; --widget-height: 20px; font-size: 15px; --input-font-size: 15px; --padding: 10px; --spacing: 10px; --slider-knob-width: 5px; --background-color: rgba(5, 0, 15, .8); --widget-color: rgba(255, 255, 255, .3); --focus-color: rgba(255, 255, 255, .4); --hover-color: rgba(255, 255, 255, .5);

--font-family: monospace;}

JAVASCRIPT:

import * as THREE from "three";import {OrbitControls} from "three/addons/controls/OrbitControls.js";import {GUI} from "https://cdn.skypack.dev/lil-gui@0.17.0";



const canvasEl = document.querySelector("#mobius");

let renderer, scene, camera, clock, orbit, strip;

const params = { stripeWidth: 1, canvasSize: 250, segments: 20, color: { r: 2, g: .5, b: -.4 },}

initScene();render();createControls();

function initScene() {

scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera(40, 1, 1, 50); camera.position.set(0, 1, 5); camera.lookAt(0, 0, 0);

clock = new THREE.Clock();

renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, canvas: canvasEl }); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.setSize(params.canvasSize, params.canvasSize);

const material = new THREE.MeshNormalMaterial({ side: THREE.DoubleSide }); material.userData.color_addition = {value: new THREE.Vector3(params.color.r, params.color.g, params.color.b)}; material.onBeforeCompile = (shader) => { shader.uniforms.color_addition = material.userData.color_addition; shader.fragmentShader = 'uniform vec3 color_addition;\n' + shader.fragmentShader; shader.fragmentShader = shader.fragmentShader.replace( ' normal ', ' normal + color_addition ' ); material.userData.shader = shader; };

orbit = new OrbitControls(camera, renderer.domElement); orbit.enablePan = false; orbit.enableZoom = false; orbit.minPolarAngle = .2 * Math.PI; orbit.maxPolarAngle = .7 * Math.PI; orbit.autoRotate = true; orbit.enableDamping = true;

strip = new THREE.Mesh(getStripGeometry(), material); scene.add(strip);}



function getStripGeometry() { const geometry = new THREE.PlaneGeometry(1, 1, 5 * params.segments, params.segments);

const positionAttr = geometry.attributes.position; const positions = positionAttr.array;

for (let i = 0; i < positions.length; i += 3) { const mobiusCoords = mobiusStripPoint(positions[i], positions[i + 1]); positions[i] = mobiusCoords[0]; positions[i + 1] = mobiusCoords[1]; positions[i + 2] = mobiusCoords[2]; }

positionAttr.copyArray(positions); geometry.rotateX(.5 * Math.PI) geometry.rotateZ(.1 * Math.PI) geometry.computeVertexNormals();

return geometry;}

function mobiusStripPoint(x, y) { const yy = y * params.stripeWidth; const angle = 2 * Math.PI * x; const r = 1 + yy * Math.cos(angle); const x1 = Math.cos(angle) * r; const y1 = Math.sin(angle) * r; const z1 = yy * Math.sin(angle); return [x1, y1, z1];}

function render(t) { orbit.update(); renderer.render(scene, camera); requestAnimationFrame(render);}

function createControls() { const gui = new GUI(); gui.add(params, "stripeWidth", .5, 1.5).onChange(() => { strip.geometry.dispose(); strip.geometry = getStripGeometry(); }).name("width"); gui.add(params.color, "r", -1, 3).onChange(v => { strip.material.userData.shader.uniforms.color_addition.value.x = v; }).name("R addition"); gui.add(params.color, "g", -1, 3).onChange(v => { strip.material.userData.shader.uniforms.color_addition.value.y = v; }).name("G addition"); gui.add(params.color, "b", -1, 3).onChange(v => { strip.material.userData.shader.uniforms.color_addition.value.z = v; }).name("B addition");}



源码:

https://codepen.io/ksenia-k/pen/NWJaBZb


体验:

https://codepen.io/ksenia-k/full/NWJaBZb



感谢您的阅读      

在看点赞 好文不断  

初识Threejs
初识 Three.js 的奇妙世界,走进三维空间,与小编一起拥抱前端技术,涉及WebGL、WebGPU、Threejs、Shader、GIS、VR/AR、数字孪生、3D图形学等。
 最新文章