废话不说,代码如下
// 绘制带数字的圆点
import {
Scene,
BufferGeometry,
Points,
PerspectiveCamera,
Vector3,
WebGLRenderer,
CanvasTexture,
ShaderMaterial,
Color,
BufferAttribute,
} from "three";
let renderer, scene, camera;
const width = window.innerWidth;
const height = window.innerHeight;
function init() {
const container = document.getElementById("app");
scene = new Scene();
camera = new PerspectiveCamera(45, width / height, 1, 10000);
camera.position.set(0, 0, 18);
camera.lookAt(scene.position);
camera.updateMatrix();
const points = createPoint();
scene.add(points);
// const helper = new AxesHelper(5);
// scene.add(helper);
renderer = new WebGLRenderer();
renderer.setSize(width, height);
container.appendChild(renderer.domElement);
}
function createPoint() {
const geometry = new BufferGeometry().setFromPoints([
new Vector3(-3, 3, 0),
new Vector3(0, 3, 0),
new Vector3(3, 3, 0),
new Vector3(-3, 0, 0),
new Vector3(0, 0, 0),
new Vector3(3, 0, 0),
new Vector3(-3, -3, 0),
new Vector3(0, -3, 0),
new Vector3(3, -3, 0),
new Vector3(0, 6, 0),
]);
const numbers = [1, 2, 3, 4, 56789, 6, 7, 768, 19, 0];
geometry.setAttribute(
"a_number",
new BufferAttribute(new Float32Array(numbers), 1)
);
const material = new createMaterial();
const points = new Points(geometry, material);
return points;
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
init();
animate();
// testAppendCanvas();
function createCanvasSpirit() {
const SIZE = 64;
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = SIZE * 10;
canvas.height = SIZE;
ctx.font = SIZE + "px serif";
ctx.fillStyle = "#000";
// 居中
ctx.textAlign = "center";
ctx.textBaseline = "middle";
for (let i = 0; i < 10; i++) {
ctx.fillText(i, SIZE * i + SIZE / 2, SIZE / 2);
}
return canvas;
}
function testAppendCanvas() {
const canvas = createCanvasSpirit();
canvas.style.border = "1px solid #000";
canvas.style.marginTop = "10px";
canvas.style.marginLeft = "10px";
const container = document.getElementById("app");
container.appendChild(canvas);
}
function createCanvasTexture() {
const canvas = createCanvasSpirit();
const texture = new CanvasTexture(canvas);
return texture;
}
function createMaterial() {
const vertexShader = `
attribute float a_number;
uniform float u_size;
varying float v_number;
void main(){
v_number = a_number;
gl_PointSize = u_size;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`;
const fragmentShader = `
uniform vec3 u_color;
uniform vec3 u_front_color;
uniform sampler2D u_number_spirit;
varying float v_number;
float numDigits(float num) {
float digits = 1.0;
while (num >= 10.0) {
num = floor( num / 10.0 );
digits += 1.0;
}
return floor(digits);
}
float getNumByDigits(float num, float digits) {
float digit = floor( mod(num / pow(10.0, digits ), 10.0) );
return digit;
}
void main() {
float dist = distance( gl_PointCoord, vec2(0.5,0.5) );
float discard_opacity = 1.0 - smoothstep( 0.48, 0.5, dist );
if( discard_opacity == 0.0 ) discard;
vec2 uv = vec2( gl_PointCoord.x,1.0- gl_PointCoord.y);
float index = mod(v_number, pow(10.0,MAX_DIGITS) );
float num_length = numDigits(index); // 数字长度
uv = clamp((uv - 0.5) * (0.8 + num_length * 0.2) + 0.5,0.0,1.0);
float num_step = 1.0 / num_length; // 每位数字占的宽度
float current_digits = floor( uv.x / num_step ); // 当前uv渲染的是第几个数字
float value = getNumByDigits(index, num_length - 1.0 - current_digits); // 需要渲染的对应数字
float x = mod(uv.x,num_step)/num_step * 0.1;
if(num_length > 1.0){
x = x/2.0 + value * 0.1 + 0.025;
}else{
x += value * 0.1;
}
float y = uv.y;
float opacity = texture2D( u_number_spirit, vec2(x,y)).a;
opacity = step(0.1,opacity) * opacity;
vec3 color = mix(u_color, u_front_color, opacity);
vec4 diffuseColor = vec4( color, 1.0 * discard_opacity );
gl_FragColor = diffuseColor;
}
`;
const texture = createCanvasTexture();
const size = 64 * window.devicePixelRatio;
const material = new ShaderMaterial({
uniforms: {
u_size: { value: size }, // 点的大小
u_color: { value: new Color(0x00ff00) }, // 背景填充色
u_front_color: { value: new Color(0xff0000) }, // 文字颜色
u_number_spirit: { value: texture }, // 数字纹理
},
defines: {
MAX_DIGITS: "3.0", // 最大支持3位数
},
vertexShader,
fragmentShader,
transparent: true,
});
return material;
}