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
    );
}
位图是灰度的

描边的shader

2dx中的描边效果是通过freetype生成的描边纹理和非描边纹理,
描边label的pixel format为AI88cocos对应代码,对于OpenGL来说,它是GL_LUMINANCE_ALPHA,即一个像素是由2个通道[luminance,alpha]组成。


上图的2个纹理数据,会分别存储在luminance和alpha中,对应的cocos代码:

  • luminance是带描边的纹理数据
  • alpha是不带描边的纹理数据

label的渲染逻辑为:

描边label使用的渲染纹理

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

  • 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
这样绘制出来的效果 = 描边纹理 - 非描边纹理

第二次:绘制字符

先重置effectType
gl_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)

你可能感兴趣的:(cocos2dx label 渲染原理分析)