我建议定义两个方向的线数,并根据UV坐标计算到一条线的距离:
float t = time; vec2 noLines = vec2(30.0, 20.0); vec2 floorUV = floor((t + vUv) * noLines); vec2 distUV = t + vUv - (floorUV+0.5) / noLines;
在线的厚度和半厚度之间平滑插值( smoothstep ),计算“饱和度”。这导致线条总是在中间具有完整的“强度”(当然,你可以试验这个例子,例如 sh*0.66 , sh*0.33 ):
smoothstep
sh*0.66
sh*0.33
float sh = 0.005; vec2 lineUV = smoothstep(sh, sh*0.5, abs(distUV));
alpha通道是两个方向的最大“饱和度”值:
float uvOutput = max(lineUV.x, lineUV.y); gl_FragColor = vec4(1.0, 0.0, 0.0, uvOutput);
请参阅示例,我将建议的更改应用于原始代码:
/* Scene Initialization */ var startTime = Date.now(); var scene = new THREE.Scene(); var width = window.innerWidth; var height = window.innerHeight; var canvas = document.getElementById('canvas'); var camera = new THREE.PerspectiveCamera(75, 1, 1, 1200); camera.position.set(0, -420, 600); camera.lookAt(new THREE.Vector3(0, 0, 0)); orbitControls = new THREE.OrbitControls(camera); var renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setSize(window.innerWidth * .2, window.innerWidth * .2); renderer.setClearColor( 0xffffff, 1); canvas.appendChild(renderer.domElement); var geometry = new THREE.TorusGeometry(200, 200, 260, 260); material = new THREE.ShaderMaterial( { uniforms: {time: { type: "f", value: Date.now() - startTime}, }, vertexShader: `attribute vec3 center; varying vec3 vCenter; varying vec2 vUv; void main() { vCenter = center; vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }`, fragmentShader: `varying vec3 vCenter; varying vec2 vUv; uniform float time; uniform sampler2D tDiffuse; void main() { float t = time; vec2 noLines = vec2(30.0, 20.0); vec2 floorUV = floor((t + vUv) * noLines); vec2 distUV = t + vUv - (floorUV+0.5) / noLines; float sh = 0.005; vec2 lineUV = smoothstep(sh, sh*0.5, abs(distUV)); float uvOutput = max(lineUV.x, lineUV.y); gl_FragColor = vec4(1.0, 0.0, 0.0, uvOutput); }`, transparent: true } ); //material.extensions.derivatives = true; material.side = THREE.DoubleSide; material.transparent = true; //material.blending = THREE.Add; material.depthTest = false; var torus = new THREE.Mesh(geometry, material); var geom = torus.geometry; geometry.sortFacesByMaterialIndex(); torus.position.x = 0; scene.add(torus); /* Request Animation Frame */ function animation() { camera.lookAt(new THREE.Vector3(0, 0, 0)); renderer.render(scene, camera); material.uniforms.time.value = (Date.now() - startTime)/20000; requestAnimationFrame(animation); } resize(); window.onresize = resize; animation(); setupDraggableEvents(); function setupDraggableEvents() { var hammer = new Hammer(document.getElementsByTagName('canvas')[0]); hammer.on('pan', function(event) { torus.rotation.y += event.velocityX / 10; torus.rotation.x += event.velocityY / 10; }); } function resize() { var aspect = window.innerWidth / window.innerHeight; renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = aspect; camera.updateProjectionMatrix(); }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js"></script> <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script> <div id="canvas"></div>