使用three.js創建大小不隨著場景變化的文字,需要以下兩步:
1、將文字繪制到畫布上,
2、創建著色器材質,把文字放到三維場景中,
優點:
1、跟用html實作文字相比,這些文字可以被模型遮擋,更具有三維效果,
2、不會隨著場景旋轉縮放改變尺寸,不存在遠處看不清的情況,適用于三維標注,
效果圖:

示例代碼1:https://github.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/object/text/UnscaledText.js
示例代碼2:https://gitee.com/tengge1/ShadowEditor/blob/master/ShadowEditor.Web/src/object/text/UnscaledText.js
實作方法
1、使用canvas繪制文字,先用黑色繪制描邊,然后用白色繪制文字,黑色描邊主要為了讓文字在白色背景處能看清,
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;
// 畫描邊
context.font = `16px "Microsoft YaHei"`;
context.strokeStyle = '#000';
context.strokeText(text, halfWidth, halfHeight);
// 畫文字
context.fillStyle = '#fff';
context.fillText(text, halfWidth, halfHeight);
2、 創建著色器材質,將文字正對螢屏,渲染到三維場景中,
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);
說明:由于canvas上繪制的文字邊緣是半透明的,材質要設定成半透明才能實作文字邊緣平滑效果,
UnscaledTextVertexShaderprecision 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 ); }
說明:
a、(0.0, 0.0, 0.0)是平面中心世界坐標,左乘modelViewMatrix和projectionMatrix后,得到螢屏坐標系中的坐標,
b、proj.x / proj.w + position.x * width / domWidth * 2.0的意思是把平板中心放到世界坐標系正確位置,讓平板顯示的寬度恰好等于螢屏上的像素數,避免文字縮放,
c、乘以2.0是因為three.js默認生成的平板寬度和高度是1,螢屏坐標系寬度和高度都是從-1到1,是2,
d、gl_Position.w為1.0時,是正投影,模型大小不隨著螢屏深度變化而改變,
UnscaledTextFragmentShaderprecision highp float; uniform sampler2D tDiffuse; uniform float width; uniform float height; varying vec2 vUv; void main() { // 注意vUv一定要從畫布整數坐標取顏色,否則會導致文字模糊問題, vec2 _uv = vec2( (floor(vUv.s * width) + 0.5) / width, (floor(vUv.t * height) + 0.5) / height ); gl_FragColor = texture2D( tDiffuse, _uv ); }
說明:
1、uv坐標一定要恰好對應畫布上的像素點,否則會導致文字模糊問題,
文字模糊的解決方法
使用three.js或WebGL繪制文字,很容易遇到文字模糊的問題,主要有以下幾個方面的原因, 1、canvas上繪制線條,是從兩個像素中心點畫的, 在整數像素處繪制1px的線條,其實在1px線條兩邊,都有0.5px半透明的線條,實際繪制了2px,繪制時,一定要從(整數+0.5px)像素開始繪制, 具體參考《canvas畫布解決1px線條模糊的問題》:https://www.jianshu.com/p/c0970eecd843 在上面的代碼中,字體大小和線寬都是偶數,不存在這個問題, 2、根據uv坐標從貼圖取色的時候,一定要恰好取到貼圖上的整數像素,否則會進行顏色插值,導致模糊, 我被這個問題卡了很久,具體現象就是隨著視角改變,文字有時候清晰,有時候模糊,一閃一閃的, 解決方法就是在片源著色器中對自動插值的uv坐標進行“取整”,恰好取到(整數+0.5像素),為什么加0.5,看上面《canvas畫布解決1px線條模糊的問題》的文章, 實作代碼:vec2 _uv = vec2( (floor(vUv.s * width) + 0.5) / width, (floor(vUv.t * height) + 0.5) / height );
其中,width和height分別是貼圖的寬度和高度, 3、gl_Position.xy恰好對應螢屏上的像素點, 這是我猜測的一個原因,根據原因2進行修改后,文字不模糊了,所以,這個沒有仔細測驗,
參考資料
1、基于three.js的開源三維場景編輯器:https://github.com/tengge1/ShadowEditor 2、canvas畫布解決1px線條模糊的問題:https://www.jianshu.com/p/c0970eecd843轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/165944.html
標籤:JavaScript
上一篇:js函式
下一篇:js物件
