IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> CocosCreator的文本是怎么渲染到网页的 -> 正文阅读

[JavaScript知识库]CocosCreator的文本是怎么渲染到网页的

摘要

CocosCreator的文本组件,输入文字后是怎么将其渲染到网页上面的?阔阔带你从头开始分析!

正文

使用工具

  • 谷歌浏览器
  • CocosCreator 版本 2.4.7

先说说怎么在网页上显示文字

1.最基本的 html 内文本显示:

浏览器中:

2.然后出现了 canvas 绘制,在 canvas 里显示文字:

你会发现渲染有点模糊,解决办法可以采用增大渲染分辨率,保持 DOM 大小,就清晰了。

3.再后来流行了 WebGL,要想在 WebGL 中显示文本,有一种办法就是先在 canvas 下渲染 2d 的文字,然后将其作为纹理传入 WebGL 进行渲染。CocosCreator 也是这么搞的,这样的方法适合动态生成文字。举个例子(可以 copy 到一个 HTML 里自己跑一下):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <canvas id="webgl" width="200"  height="200"></canvas>

    <script type="text/javascript">
        // 创建一个 canvas 用于渲染文本
        const canvasDom = document.createElement("canvas")
        canvasDom.width = 200
        canvasDom.height = 200
        const ctx = canvasDom.getContext("2d")

        ctx.textBaseline = "top"
        ctx.font="20px 微软雅黑"
        // 绘制文本
        ctx.fillText("显示文本-KUOKUO", 0, 0)

        // 在 WebGL 中渲染出来写的就比较复杂,需要先创建 shader
        const webglDom = document.getElementById('webgl')
        // 初始化 WebGL 环境
        const gl = webglDom.getContext("webgl")

        const fs = gl.createShader(gl.FRAGMENT_SHADER)
        const fsText = `
            precision mediump float;
            uniform sampler2D u_texture;
            varying vec2 v_texCoord;

            void main() {
                gl_FragColor = texture2D(u_texture, v_texCoord);
            }
        `
        gl.shaderSource(fs, fsText)
        gl.compileShader(fs)

        const vs = gl.createShader(gl.VERTEX_SHADER)
        const vsText = `
            attribute vec2 a_position;
            attribute vec2 a_texCoord;
            uniform vec2 u_resolution;
            varying vec2 v_texCoord;

            void main() {
                vec2 rate = a_position / u_resolution;
                // 转换坐标空间到 -1 -> +1
                vec2 clip = (rate * 2.0) - 1.0;
                gl_Position = vec4(clip * vec2(1, -1), 0, 1);
                v_texCoord = a_texCoord;
            }
        `
        gl.shaderSource(vs, vsText)
        gl.compileShader(vs)

        const program = gl.createProgram()
        gl.attachShader(program, fs)
        gl.attachShader(program, vs)
        gl.linkProgram(program)

        const positionLocation = gl.getAttribLocation(program, "a_position")
        const texcoordLocation = gl.getAttribLocation(program, "a_texCoord")

        // 矩形图像
        const positionBuffer = gl.createBuffer()
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
            0, 0,
            200, 0,
            0, 200,
            0, 200,
            200, 0,
            200, 200,
        ]), gl.STATIC_DRAW)
        
        // 顶点空间
        const texcoordBuffer = gl.createBuffer()
        gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer)
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
            0.0,  0.0,
            1.0,  0.0,
            0.0,  1.0,
            0.0,  1.0,
            1.0,  0.0,
            1.0,  1.0,
        ]), gl.STATIC_DRAW)

        const texture = gl.createTexture()
        gl.bindTexture(gl.TEXTURE_2D, texture)

        // 使得不是 2 整数次幂的图像也可以渲染
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)

        // 注意在这里传入了 canvas !!!
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvasDom)

        gl.viewport(0, 0, 200, 200)
        gl.clearColor(0, 0, 0, 0)
        gl.clear(gl.COLOR_BUFFER_BIT)

        gl.useProgram(program)
        const resolutionLocation = gl.getUniformLocation(program, "u_resolution")
        // 将参数传入 shader
        gl.uniform2f(resolutionLocation, 200, 200)
        gl.enableVertexAttribArray(positionLocation)
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)
        gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0)
        gl.enableVertexAttribArray(texcoordLocation)
        gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer)
        gl.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0)

        // 最终绘制
        gl.drawArrays(gl.TRIANGLES, 0, 6)
    </script>
</body>
</html>

浏览器中的效果:

CocosCreator 是怎么做的

观察上面的代码,首先文字的大小应该是根据内容进行变化的,而不是像我这样定死一个 200 长度,对于文字大小的测量方法:

打开 CocosCreator 的 v2.4.7 的源码,在 text-utils.js 脚本中有着 safeMeasureText 这个方法,其中就是测量方法,然后还加了缓存,同样的 key 就不用再测了,见下图:

再转到源码中的 letter-font.js 脚本,看下 updateRenderData 这个方法,这个就是装载文字显示数据的方法。在这个方法里执行了 _updateProperties_updateTexture 这两个方法,分别看一看:

先看第一个,对照下面的代码,Label._canvasPool.get() 其实就是 document.createElement("canvas") 的一层封装,获取到了一个 canvas 对象。然后,调用测量方法传入文本获得占据宽高,最后执行 this._texture.initWithElement(this._canvas) 创建了纹理。

再看看 _updateTexture 干了啥:

不难发现,就是对文字的样式、颜色等进行修改,最终调用了 fillText 绘制文字,然后调用了 this._texture.handleLoadedTexture();,再转到源码 CCTexture2D.js 中可以看到这个方法:

在上面这个方法了装入纹理参数,像素格式。

再深入点!!!

看到这里,大概的流程通了,但是 CocosCreator 怎么把文本显示出来的?那些 shader 中的 useProgram 还有 buffer 相关的其他操作呢?

在上面的 handleLoadedTexture 方法中有这样一句代码:

this._texture = new renderer.Texture2D(renderer.device, opts);

renderer.device 是什么?打开源码目录中 cocos2d/core/renderer/index.js 脚本,其中的 initWebGL 方法就是环境初始化。在 CCGame.js_initRenderer 方法中判断了 canvas 环境还是 webgl 环境!

然后,在 initWebGL 这个方法中有这么一行代码:

this.device = new gfx.Device(canvas, opts);

挖到 device.js 脚本中看看,在 Device 类的构造函数中也终于找到了我们想要的东西:

然后你会发现 device.js 这个脚本其实就是对 gl 一系列方法的封装,在最后面的 draw 方法中有 shader 的 buffer 操作等一系列方法的封装以及最终绘制方法:

最终调用的绘制方法:

好了,gl 的渲染方法找到了,还差最后一步,它是怎么被调用到的呢?

官方文档有 渲染流 这么一块介绍,渲染流(RenderFlow)是 v2.0 新加的流程,它的作用是可以剔除无用的渲染分支,只进入预先创建好的渲染分支,这样可以有效减少非常多的动态判断。

在 CocosCreator 中的渲染会走到 RenderFlow,在 render-flow.js 中需要渲染的会走入 render 方法!

回过来,在 base-renderer.js 源码中,_draw 正是在设置好一系列 gl 参数后调用了 device 的 draw 方法之中。而 ForwardRenderer 继承自 BaseRenderer,在 render 时就会触发 _draw 方法,将文字渲染出来。

一切豁然开朗!!!

更多文章与分享

个人网站:www.kuokuo666.com

2022!Day Day Up!

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-11 23:54:12  更:2022-01-11 23:54:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:35:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码