[✔️] cocos2dx label 渲染原理分析

简介: [✔️] cocos2dx label 渲染原理分析

不描边


cocos2d-x\cocos\renderer\ccShader_Label_normal


attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
void main()
{
    gl_Position = CC_MVPMatrix * a_position;
    v_fragmentColor = a_color;
    v_texCoord = a_texCoord;
}


varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform vec4 u_textColor;
void main()
{
    // rgb来自u_textColor,是因为字符位图是灰度的,最终被视为Alpha了
    gl_FragColor =  v_fragmentColor * vec4(u_textColor.rgb,// RGB from uniform
        u_textColor.a * texture2D(CC_Texture0, v_texCoord).a// A from texture & uniform
    );
}


image.png


使用的是A8,一个像素只有一个通道,渲染的时候根据alpha就能知道label的样子


描边的shader


描边label的pixel format为AI88

2dx中的描边效果的纹理是通过freetype生成的描边纹理和非描边纹理后,再合并为一个AI88的纹理,即一个像素是由2个通道[luminance,alpha]组成。


image.png


上图的2个纹理数据,会分别存储在AI88的luminance和alpha中:


  • 一个通道:luminance是带描边的纹理数据


  • 一个通道:alpha是不带描边的纹理数据


对于OpenGL来说,它是GL_LUMINANCE_ALPHA


image.png


label的渲染逻辑为:


image.png


image.png


上图中的结果正好是和这个对应的:


  • 1是都不在描边纹理和非描边纹理范围,所以lum=0,alpha=0


  • 2是描边纹理的范围,所以lum=255,不在非描边纹理范围,所以alpha=0


  • 3是描边纹理和非描边纹理的交集,所以lum=alpha=255


另外,位图数据在轮廓边缘的值是介于0~255的


  • 4是不带描边的轮廓数据,它的alpha不是255,


  • 5是带描边的轮廓数据,它的Lum不是255


在shader里面,边缘是通过纹理的alpha识别的,因为位图轮廓是灰度的


GL_LUMINANCE_ALPHA的[L,A]数据最终在着色器采样后,转换为RGBA为[L,L,L,A]


此时的alpha刚好为非描边纹理的轮廓边缘


varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform vec4 u_effectColor;
uniform vec4 u_textColor;
uniform int u_effectType;
void main()
{
    vec4 sample = texture2D(CC_Texture0, v_texCoord);
    // fontAlpha == 1 means the area of solid text (without edge)
    // fontAlpha == 0 means the area outside text, including outline area
    // fontAlpha == (0, 1) means the edge of text
    float fontAlpha = sample.a;
    // outlineAlpha == 1 means the area of 'solid text' and 'solid outline'
    // outlineAlpha == 0 means the transparent area outside text and outline
    // outlineAlpha == (0, 1) means the edge of outline
    float outlineAlpha = sample.r;
    if (u_effectType == 0) // draw text
    {
        gl_FragColor = v_fragmentColor * vec4(u_textColor.rgb, u_textColor.a * fontAlpha);
    }
    else if (u_effectType == 1) // draw outline
    {
        // multipy (1.0 - fontAlpha) to make the inner edge of outline smoother and make the text itself transparent.
        gl_FragColor = v_fragmentColor * vec4(u_effectColor.rgb, u_effectColor.a * outlineAlpha * (1.0 - fontAlpha));
    }
    else // draw shadow
    {
        gl_FragColor = v_fragmentColor * vec4(u_effectColor.rgb, u_effectColor.a * outlineAlpha);
    }
}


第一次:绘制outline


gl_FragColor = v_fragmentColor * vec4(u_effectColor.rgb, u_effectColor.a * L* (1.0 - A));


  • 区域1因为L=A=0,alpha=effectColor.a * 0 * (1-0)=0


  • 区域2因为L=255,A=0,alpha=effectColor.a * 1 * (1-0)=effectColor.a


  • 区域3因为L=255,A=255,alpha=effectColor.a * 1 * (1-1) = 0


这样绘制出来的效果 = 描边纹理 - 非描边纹理


第二次:绘制字符


重置effectTypegl_FragColor = v_fragmentColor * vec4(u_textColor.rgb, u_textColor.a * A);只有A参与计算,那么也就只有区域3和4会显示出来了


PositionTextureColor_noMVP


ccShader_PositionTextureColor_noMVP.frag


attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
void main()
{
    gl_Position = CC_PMatrix * a_position;
    v_fragmentColor = a_color;
    v_texCoord = a_texCoord;
}


varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
void main()
{
    gl_FragColor = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
}


a_color来自node.color,字符纹理变成白色即可


PositionTexture


ccShader_PositionTexture.vert


attribute vec4 a_position;
attribute vec2 a_texCoord;
#ifdef GL_ES
varying mediump vec2 v_texCoord;
#else
varying vec2 v_texCoord;
#endif
void main()
{
    gl_Position = CC_MVPMatrix * a_position;
    v_texCoord = a_texCoord;
}


ccShader_PositionTexture.frag


#ifdef GL_ES
precision lowp float;
#endif
varying vec2 v_texCoord;
void main()
{
    gl_FragColor =  texture2D(CC_Texture0, v_texCoord);
}


矩阵的源头


// 单位矩阵:相当于数的乘法中的1
const Mat4 Mat4::IDENTITY = Mat4(
                    1.0f, 0.0f, 0.0f, 0.0f,
                    0.0f, 1.0f, 0.0f, 0.0f,
                    0.0f, 0.0f, 1.0f, 0.0f,
                    0.0f, 0.0f, 0.0f, 1.0f);
void GLView::renderScene(Scene* scene, Renderer* renderer){
  scene->render(renderer,  Mat4::IDENTITY,nullptr);
}


Label::onDraw(const Mat4& transform)
目录
相关文章
|
5月前
|
监控 前端开发 JavaScript
如何开发一套工程项目部管理系统?(附架构图+流程图+代码参考)
工程项目部管理系统通过信息化手段整合进度、资源、人员、财务及风险管理,提升项目执行效率与决策质量。系统涵盖功能设计、业务流程、开发技巧及实现效果,助力企业构建高效、低风险的管理平台,实现项目全流程监控与优化。
|
12月前
|
人工智能 文字识别 异构计算
NVIDIA-Ingest:英伟达开源智能文档提取及结构化工具,支持 GPU 加速和并行处理
NVIDIA-Ingest 是英伟达开源的智能文档提取工具,支持 PDF、Word、PPT 等多种格式,提供并行处理和 GPU 加速,适用于企业内容管理和生成式应用。
564 18
NVIDIA-Ingest:英伟达开源智能文档提取及结构化工具,支持 GPU 加速和并行处理
|
10月前
|
人工智能
一场静默的教育革命正在发生:AI如何重塑学习与教学
生成式人工智能(Generative AI)正深刻改变教育领域,从学生到职场人士,AI逐渐成为必备技能。文章探讨了AI在教育中的应用与挑战,如认知卸载现象及批判性思维能力下降,并提出通过GAI认证提升AI技能的标准化途径。未来教育将形成“师、机、生”三元结构,强调人与AI协作共进。掌握AI技术不仅是职业发展的关键,更是教育革命中的核心推动力。
|
Linux Shell
10-10|linux命令查询 关键字在文本中出现的行数
10-10|linux命令查询 关键字在文本中出现的行数
|
存储 机器学习/深度学习 并行计算
GPU通信互联技术:GPUDirect、NVLink与RDMA
在高性能计算和深度学习领域,GPU已成为关键工具。然而,随着模型复杂度和数据量的增加,单个GPU难以满足需求,多GPU甚至多服务器协同工作成为常态。本文探讨了三种主要的GPU通信互联技术:GPUDirect、NVLink和RDMA。GPUDirect通过绕过CPU实现GPU与设备直接通信;NVLink提供高速点对点连接和支持内存共享;RDMA则在网络层面实现直接内存访问,降低延迟。这些技术各有优势,适用于不同场景,为AI和高性能计算提供了强大支持。
|
机器学习/深度学习 算法 计算机视觉
【YOLOv8改进-损失函数】SlideLoss损失函数,解决样本不平衡问题
YOLO-FaceV2是基于YOLOv5的实时人脸检测模型,采用RFE模块增强小人脸检测,NWD损失处理定位偏差,SEAM注意力模块应对遮挡,Slide Loss解决样本不平衡,提升对难样本的关注。在WiderFace数据集上超越YOLO系列。论文和代码已公开。Slide Loss通过IoU加权,优化边界样本,提高模型性能。
|
存储 弹性计算 运维
中小企业使用阿里云无影云电脑可以做哪些事情?
阿里云无影云电脑为中小企业提供低成本、弹性、安全的桌面服务。用户可随时随地访问,灵活调整配置,便捷分享文件,确保数据安全,并简化运维管理。现推出优惠,如4核8G内存版本低至98.99元/年,8核16G仅398元/年。
460 0
|
NoSQL Linux Shell
【进程概念】进程状态以及僵尸进程(结合代码)
【进程概念】进程状态以及僵尸进程(结合代码)