【问题标题】:Radial gradient - procedural texture for cocos2d-x v3.15+径向渐变 - cocos2d-x v3.15+ 的程序纹理
【发布时间】:2017-09-11 22:52:23
【问题描述】:

在 cocos2d-x 中there is already 一个LayerRadialGradient。 效果很好,但是我的游戏场景上运行了其他着色器,所以现在出现了问题。 It's using a shader.

我想用生成径向渐变纹理的 Sprite 替换它。

这里是LayerRadialGradient绘制结果的例子:

LayerRadialGradient::create( 
  Color4B(179, 232, 184, 89),
  Color4B(0, 90, 128, 0),
  620,
  center,
  0.0f);

我有一些代码建议,例如:

static Color4B Lerp(const Color4B& value1, const Color4B& value2, float amount)
{
    amount = clampf(amount, 0.0f, 1.0f);
    return Color4B(
        (int)MathUtil::lerp(value1.r, value2.r, amount),
        (int)MathUtil::lerp(value1.g, value2.g, amount),
        (int)MathUtil::lerp(value1.b, value2.b, amount),
        (int)MathUtil::lerp(value1.a, value2.a, amount)
    );
}

static float Falloff(float distance, float maxDistance, float scalingFactor)
{
    if (distance <= maxDistance / 3)
    {
        return scalingFactor * (1 - 3 * distance * distance / (maxDistance * maxDistance));
    }
    else if (distance <= maxDistance)
    {
        float x = 1 - distance / maxDistance;
        return (3.f / 2.f) * scalingFactor * x * x;
    }
    else
        return 0;
}

static float Falloff(float distance, float maxDistance)
{
    return Falloff(distance, maxDistance, 1.f);
}

static Texture2D* generateRadialGradientTexture(int size) {
    auto radius = size / 2;
    auto colors = new (std::nothrow) GLubyte[size * size * 4];

    Color4B centerColor(Color4B(179, 232, 184, 89));
    Color4B borderColor(Color4B(0, 90, 128, 0));        

    for (int y = 0; y < size; y++)
    {
        for (int x = 0; x < size; x++)
        {
            float distance = Vec2::ONE.distance(Vec2(x, y) / radius);
            float alpha = Falloff(distance, 1, 1);

            int idx = (y * size + x) * 4;
            float innerGradient = Falloff(distance, 0.6f, 0.8f);
            auto color = Lerp(borderColor, centerColor, innerGradient);

            colors[idx + 0] = color.r;
            colors[idx + 1] = color.g;
            colors[idx + 2] = color.b;

            //alpha
            colors[idx + 3] = (GLbyte)clampf(alpha * 256.f + 0.5f, 0.f, 255.f);
        }
    }

    auto txt = new Texture2D();
    txt->initWithData(colors, size * size * 4, Texture2D::PixelFormat::RGBA8888, size, size, Size(size, size));

    delete[] colors;
    return txt;
}

只使用:

Sprite::createWithTexture(generateRadialGradientTexture(radius));

但结果真的不一样:

【问题讨论】:

    标签: textures shader procedural-generation radial-gradients cocos2d-x-3.x


    【解决方案1】:

    我调查了shader code 的问题,并在 C++ 中开发了一个类似的算法。该算法在两种颜色之间进行线性插值,从中心点开始,使用内部颜色,径向向外到外部颜色。
    C++ 算法生成一个大小由输入参数指定的纹理:

    #include <vector>  // std::vector
    #include <math.h>  // sqrt
    
    Texture2D * TextureRadialGradientCreate(
        int            widht,
        int            height,
        const Color4B &startColor,
        const Color4B &endColor,
        float          radius,
        const Vec2    &center,
        float          expand )
    {
        Vec4 sCol( startColor.r / 255.0, startColor.g / 255.0, startColor.b / 255.0, startColor.a / 255.0 );
        Vec4 eCol( endColor.r / 255.0, endColor.g / 255.0, endColor.b / 255.0, endColor.a / 255.0 );
    
        std::vector<unsigned char> plane( widht * height * 4, 0 );
        for ( int y = 0; y < height; ++ y )
        {
            for ( int x = 0; x < widht; ++ x )
            {
                 float dx = x - center.x;
                 float dy = y - center.y;
                 float d = sqrt( dx*dx + dy*dy ) / radius;
    
                 Vec4 mixCol( 0.0f, 0.0f, 0.0f, 0.0f );
                 if ( expand < 1.0f && d < 1.0f )
                 {
                     float a = ( d - expand ) / ( 1.0 - expand );
                     mixCol = (d <= expand) ? sCol : ( 1.0 - a ) * sCol + a*eCol;
                 }
    
                 size_t i = ( y * widht + x ) * 4;
                 plane[i+0] = (unsigned char)(mixCol.x * 255.0f);
                 plane[i+1] = (unsigned char)(mixCol.y * 255.0f);
                 plane[i+2] = (unsigned char)(mixCol.z * 255.0f);
                 plane[i+3] = (unsigned char)(mixCol.w * 255.0f);
            }
        }
    
        Texture2D *texture = new Texture2D();
        if ( texture != nullptr )
            texture->initWithData( plane.data(), plane.size() / 4, Texture2D::PixelFormat::RGBA8888, widht, height, cocos2d::Size(widht, height) );
        return texture;
    }
    

    例如一个大小为 600*600 的二次纹理,纹理中心有一个渐变高光:

    int w = 600;
    int h = 600;
    float rad = ( w < h ? w : h ) / 2.0f;
    Vec2 cpt( w / 2.0f, h / 2.0f );
    texture = TextureRadialGradientCreate( w, h, layer_startColor, layer_endColor, rad, cpt, layer_expand );
    


    答案的扩展

    我不知道,也许径向着色器可以优化?

    通过使用 GLSL clamp 函数,通过将 mix 函数的插值参数限制在 (0, 1) 范围内,可以避免条件分支。
    我建议像这样优化fragment shader

    #ifdef GL_ES
    varying lowp vec4 v_position;
    #else
    varying vec4 v_position; 
    #endif
    
    uniform vec4 u_startColor;
    uniform vec4 u_endColor;
    uniform vec2 u_center;
    uniform float u_radius;
    uniform float u_expand;
    
    void main()
    {
        float d = distance(v_position.xy, u_center) / u_radius;
        float a = (d - u_expand) / (1.0 - u_expand);
        gl_FragColor = mix( u_startColor, u_endColor, clamp(0.0, 1.0, a) ); 
    }
    

    【讨论】:

    • 谢谢。它的效果非常好。但是我忘了提到我会在update方法中使用它。 So it's really poor performance,我的游戏现在是 5fps..
    • 当我同时使用这两个着色器时,问题就出现了。一个众所周知的是from the my previous question,另一个只是在同一场景中添加LayerRadialGradient。在我的 iPod Touch 5G 上运行它们时大约 45fps 和抖动。但是,如果我删除渐变,我会得到稳定的 60fps。我不知道,也许径向着色器可以优化?我需要始终为正方形,expand 始终为 0,u_center 始终为 (0.5,0.5)。也许我应该为此创建另一个问题..?
    • 在这种情况下,我使用的是 updateWithData,就像我在着色时所做的一样,它在update 中运行得非常快,这让我的颜色到颜色的变化变得流畅而漂亮。使用新的着色器后,我的性能从 30-32fps 提高到 38-40fps,还不错,但仍然不够。
    • 我已经尝试为我的渐变着色,效果很好(但我在渐变方面遇到了一些问题,它是条带),但是当场景中的精灵过多时,50fps 和抖动再次表现不佳。无论如何,谢谢你的回答!我可能会尝试生成渐变并在删除条带后对其进行着色(奇怪,但是当在 Adob​​e Ai 中创建的具有相同颜色的相同渐变会产生条带,但使用具有相同大小和颜色的 LayerRadialGradient 创建时几乎不会产生任何条带。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多