1.实现圆环绘制
// fragmentshader
void main() {
float radius = 0.5;
vec2 st = gl_PointCoord.xy;
float dist = distance(st,vec2(0.5,0.5));
float alpha = smoothstep(radius-0.12,radius,dist) - smoothstep(radius-0.1,radius,dist);
gl_FragColor = vec4( 1.0,0.0,0.0, alpha);
}
2.让圆环存在一个1/4的缺口(具体大小可以自己调整)
// fragmentshader
#define PI 3.14159265359
void main() {
float radius = 0.5;
vec2 st = gl_PointCoord.xy;
float dist = distance(st,vec2(0.5,0.5));
float alpha = smoothstep(radius-0.12,radius,dist) - smoothstep(radius-0.1,radius,dist);
float theta = mod( atan(st.y - 0.5,st.x - 0.5) + PI , PI*2.0);
float a = step( PI/2.0, theta );
gl_FragColor = vec4( 1.0,0.0,0.0, alpha*a);
}
完整html的demo:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<script type="x-shader/x-vertex" id="vertexshader">
attribute float aSize;
attribute float aStartAngle;
varying float vStartAngle;
void main() {
vStartAngle = aStartAngle;
gl_PointSize = aSize;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
uniform float uTheta;
varying float vStartAngle;
void main() {
float radius = 0.5;
vec2 st = gl_PointCoord.xy;
float dist = distance(st,vec2(0.5,0.5));
float alpha = smoothstep(radius-0.15,radius,dist) - smoothstep(radius-0.05,radius,dist);
float theta = mod( atan(st.y - 0.5,st.x - 0.5) + PI + vStartAngle + uTheta , PI*2.0);
float a = step( PI/2.0, theta );
gl_FragColor = vec4( 1.0,0.0,0.0, alpha*a);
}
</script>
<script type="module">
import * as THREE from "https://unpkg.com/three@0.155.0/build/three.module.js";
// import * as THREE from './three.module.js'
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const geometry = new THREE.BufferGeometry();
const positions = [];
const sizes = [];
const startAngles = [];
for (let i = 0; i < 10; i++) {
// position
for (let s = 0; s < 3; s++) {
positions.push(Math.random() * 5 - 2.5);
}
// size
sizes.push(20 + Math.random() * 30);
// startAngle 让每个圆环不要看起来是同步的
startAngles.push(Math.random() * Math.PI * 2);
}
geometry.setAttribute(
"position",
new THREE.Float32BufferAttribute(positions, 3)
);
geometry.setAttribute(
"aSize",
new THREE.Float32BufferAttribute(sizes, 1)
);
geometry.setAttribute(
"aStartAngle",
new THREE.Float32BufferAttribute(startAngles, 1)
);
const material = new THREE.ShaderMaterial({
vertexShader: document.getElementById("vertexshader").textContent,
fragmentShader: document.getElementById("fragmentshader").textContent,
uniforms: {
uTheta: { value: (Date.now() * 0.01) % (Math.PI * 2) },
},
defines: {
PI: Math.PI,
},
transparent: true,
});
const points = new THREE.Points(geometry, material);
scene.add(points);
camera.position.z = 5;
function animate() {
requestAnimationFrame(animate);
material.uniforms.uTheta.value = (Date.now() * 0.01) % (Math.PI * 2);
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>