Using three.js to create text whose size does not change with the scene requires the following two steps:
1. Draw the text on the canvas.
2. Create a shader material and put the text in the 3D scene.
Advantage:
1. Compared with html, these texts can be occluded by the model and have more 3D effect.
2. It does not change the size with the rotation and scale of the scene. It is suitable for 3D annotation because it does not have the situation that you cannot see clearly in the distance.
Design sketch:
Example code 1: https://github.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/object/text/UnscaledText.js
Example code 2: https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/object/text/UnscaledText.js
Implementation method
1. Use canvas to draw the text, first draw the edge with black, then draw the text with white. Black stroke is mainly used to make the text clear on the white background.
let context = canvas.getContext('2d'); context.imageSmoothingQuality = 'high'; context.textBaseline = 'middle'; context.textAlign = 'center'; context.lineWidth = 4; let halfWidth = canvas.width / 2; let halfHeight = canvas.height / 2; // Drawing stroke context.font = `16px "Microsoft YaHei"`; context.strokeStyle = '#000'; context.strokeText(text, halfWidth, halfHeight); // Draw text context.fillStyle = '#fff'; context.fillText(text, halfWidth, halfHeight);
2. Create a shader material, face the text to the screen, and render it into a 3D scene.
let geometry = new THREE.PlaneBufferGeometry(); let material = new THREE.ShaderMaterial({ vertexShader: UnscaledTextVertexShader, fragmentShader: UnscaledTextFragmentShader, uniforms: { tDiffuse: { value: new THREE.CanvasTexture(canvas) }, width: { value: canvas.width }, height: { value: canvas.height }, domWidth: { value: renderer.domElement.width }, domHeight: { value: renderer.domElement.height } }, transparent: true }); let mesh = new THREE.Mesh(geometry, material);
Note: because the text edge painted on canvas is translucent, the material should be set to translucent to achieve the text edge smoothing effect.
precision highp float; uniform float width; uniform float height; uniform float domWidth; uniform float domHeight; varying vec2 vUv; void main() { vUv = uv; vec4 proj = projectionMatrix * modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0); gl_Position = vec4( proj.x / proj.w + position.x * width / domWidth * 2.0, proj.y / proj.w + position.y * height / domHeight * 2.0, proj.z / proj.w, 1.0 ); }
Explain:
a, (0.0, 0.0, 0.0) is the world coordinate of the plane center. Multiply the modelViewMatrix and projectionMatrix to the left to get the coordinates in the screen coordinate system.
b. proj.x / proj.w + position.x * width / domWidth * 2.0 means to put the center of the flat panel in the correct position of the world coordinate system, so that the width of the flat panel display is exactly equal to the number of pixels on the screen, avoiding text scaling.
c. multiply by 2.0 because the width and height of the panel generated by three.js by default is 1, and the width and height of the screen coordinate system are from - 1 to 1, which is 2.
When d, gl_Position.w is 1.0, it is a positive projection, and the size of the model does not change with the depth of the screen.
precision highp float; uniform sampler2D tDiffuse; uniform float width; uniform float height; varying vec2 vUv; void main() { // Be careful vUv Be sure to take the color from the integer coordinates of the canvas, otherwise the text will be blurred. vec2 _uv = vec2( (floor(vUv.s * width) + 0.5) / width, (floor(vUv.t * height) + 0.5) / height ); gl_FragColor = texture2D( tDiffuse, _uv ); }
Explain:
1. The uv coordinate must exactly correspond to the pixel points on the canvas, otherwise the text will be blurred.
The solution of text blur
vec2 _uv = vec2( (floor(vUv.s * width) + 0.5) / width, (floor(vUv.t * height) + 0.5) / height );