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!

Cocos Creator 中,你可以使用自定义材质来实现顶点着色渲染。顶点着色渲染是一种在渲染管线中对模型的每个顶点进行颜色计算的技术,通过改变顶点的颜色来实现特定效果。下面是一个简单的示例代码: ```javascript // 在节点上添加一个脚本组件 VertexColor.js cc.Class({ extends: cc.Component, properties: { color: cc.Color.WHITE, // 自定义颜色 }, // 在渲染组件的 updateMaterial 方法中设置材质的属性 updateMaterial: function() { var material = this.getComponent(cc.RenderComponent).getMaterial(0); // 获取渲染组件的材质 material.setProperty('u_color', this.color); // 设置自定义颜色属性 }, // 在 onLoad 方法中注册 updateMaterial 方法到渲染组件的 updateMaterial 方法中 onLoad: function() { this.getComponent(cc.RenderComponent).updateMaterial = this.updateMaterial.bind(this); }, }); ``` 在上述代码中,我们假设节点上有一个渲染组件(如 Sprite、Mesh 等),我们通过自定义脚本组件 `VertexColor.js` 来实现顶点着色渲染。脚本组件中定义了一个 `color` 属性,用于设置自定义颜色。在 `updateMaterial` 方法中,我们获取渲染组件的材质,并设置自定义颜色属性。最后,在 `onLoad` 方法中将 `updateMaterial` 方法注册到渲染组件的 `updateMaterial` 方法中,以便在每帧更新时调用。 然后,你可以在 Cocos Creator 编辑器中将该脚本组件 `VertexColor.js` 添加到需要进行顶点着色渲染的节点上。在脚本组件的属性面板中,可以设置自定义的颜色值。 需要注意的是,顶点着色渲染需要使用支持顶点着色的材质和渲染组件,例如使用自定义的 Shader 或在材质中设置相应的 Uniform 属性等。具体的实现方式和效果可以根据你的需求和场景进行自定义调整。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大家好我是阔阔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值