gpt4 book ai didi

c++ - 即时重新着色 Sprite

转载 作者:行者123 更新时间:2023-12-01 22:17:51 25 4
gpt4 key购买 nike

我需要替换 Sprite 的颜色。
在谷歌建立的一些例子
image

这是我找到了一个看起来像 Unity 的工作解决方案 - [如何使用着色器动态交换 Sprite 的颜色][2]

如何将其移植到 cocos2d-x?有人可以帮忙提供代码示例吗?

我正在寻找 cocos2d-x v3 代码片段。真的很期待得到一些帮助。

最佳答案

文章中的算法How to Use a Shader to Dynamically Swap a Sprite's Colors很简单。它基于具有 256 个条目的一维查找表。这允许算法仅映射 256 种不同的颜色。

详细地说,新颜色(用于替换的颜色)存储在具有 256 个条目的一维纹理中。当从原始纹理中读取颜色时,会使用一个键来查找一维交换纹理中的新颜色。使用的关键是原始颜色的红色 channel ,这意味着原始文本中所有不同的颜色也必须具有不同的红色值。这是另一个限制。
原始文档( How to Use a Shader to Dynamically Swap a Sprite's Colors )说:

Note that this may not work as expected if two or more colors on the sprite texture share the same red value! When using this method, it's important to keep the red values of the colors in the sprite texture different.



此外,该算法通过交换颜色的 alpha channel 混合原始颜色和交换颜色。这导致如果交换颜色完全不透明则绘制交换颜色,如果交换颜色完全透明则绘制原始颜色,两者之间将被线性插值。

使用此算法的 GLSL 函数非常短,看起来像这样:
uniform sampler2D u_spriteTexture; // sprite texture 
uniform sampler1D u_swapTexture; // lookup texture with swap colors

vec4 SwapColor( vec2 textureCoord )
{
vec4 originalColor = texture( u_spriteTexture, textureCoord.st );
vec4 swapColor = texture( u_swapTexture, originalColor.r );
vec3 finalColor = mix( originalColor.rgb, swapColor.rgb, swapColor.a );
return vec4( finalColor.rgb, originalColor.a );
}

建议算法

阅读建议 shader从这个问题,我想出了以下解决方案。着色器正在使用一种算法将 RGB 转换为 hue, saturation, and value然后回来。我接受了这个想法并介绍了我自己的想法。

RGB 和 HSV 之间的高性能转换函数可以在 RGB to HSV/HSL/HCY/HCL in HLSL 找到,它可以很容易地从 HLSL 转换为 GLSL:

RGB 转 HSV
const float Epsilon = 1e-10;

vec3 RGBtoHCV( in vec3 RGB )
{
vec4 P = (RGB.g < RGB.b) ? vec4(RGB.bg, -1.0, 2.0/3.0) : vec4(RGB.gb, 0.0, -1.0/3.0);
vec4 Q = (RGB.r < P.x) ? vec4(P.xyw, RGB.r) : vec4(RGB.r, P.yzx);
float C = Q.x - min(Q.w, Q.y);
float H = abs((Q.w - Q.y) / (6.0 * C + Epsilon) + Q.z);
return vec3(H, C, Q.x);
}

vec3 RGBtoHSV(in vec3 RGB)
{
vec3 HCV = RGBtoHCV(RGB);
float S = HCV.y / (HCV.z + Epsilon);
return vec3(HCV.x, S, HCV.z);
}

HSV 转 RGB
vec3 HUEtoRGB(in float H)
{
float R = abs(H * 6.0 - 3.0) - 1.0;
float G = 2.0 - abs(H * 6.0 - 2.0);
float B = 2.0 - abs(H * 6.0 - 4.0);
return clamp( vec3(R,G,B), 0.0, 1.0 );
}

vec3 HSVtoRGB(in vec3 HSV)
{
vec3 RGB = HUEtoRGB(HSV.x);
return ((RGB - 1.0) * HSV.y + 1.0) * HSV.z;
}

与此答案的第一个算法一样,再次需要一维查找表。但是查找表的长度不必正好是 256,它完全取决于用户。关键不是红色 channel ,而是色调值,它是颜色的清晰表达,可以轻松计算,如 RGBtoHSV 所示。和 RGBtoHSV .然而,查找表必须包含在原始颜色的*色调*范围内线性分布的颜色分配。

该算法可以通过以下步骤定义:
  • 将原始颜色转换为原始色相、饱和度和值
  • 使用原始色调作为关键字在查找表中查找交换颜色
  • 将交换颜色转换为交换色调、饱和度和值
  • 将交换颜色的色调和原始饱和度和值转换为新的 RGB 颜色
  • 通过交换颜色的 alpha channel 混合原始颜色和新颜色

  • 使用此算法,通过保持原始颜色的饱和度和值,可以交换任何 RGB 颜色。请参阅以下简短明了的 GLSL 函数:
    uniform sampler2D u_spriteTexture; // sprite texture 
    uniform sampler1D u_swapTexture; // lookup texture with swap colors
    // the texture coordinate is the hue of the original color

    vec4 SwapColor( vec2 textureCoord )
    {
    vec4 originalColor = texture( u_spriteTexture, textureCoord.st );
    vec3 originalHSV = RGBtoHSV( originalColor.rgb );
    vec4 lookUpColor = texture( u_swapTexture, originalHSV.x );
    vec3 swapHSV = RGBtoHSV( lookUpColor.rgb );
    vec3 swapColor = HSVtoRGB( vec3( swapHSV.x, originalHSV.y, originalHSV.z ) );
    vec3 finalColor = mix( originalColor.rgb, swapColor.rgb, lookUpColor.a );
    return vec4( finalColor.rgb, originalColor.a );
    }

    适用于 cocos2d-x v3.15

    为了将着色器应用于 cocos2d-x v3.15,我在 cocos2d-x v3.15 测试项目的项目 cpp-empty-test 中修改了 HelloWorldScene.h 和 HelloWorldScene.cpp。
    着色器可以应用于任何 Sprite ,最多可以交换 10 种颜色,但这可以很容易地扩展。请注意,着色器不仅会更改单一颜色,还会搜索与颜色相似的所有颜色,甚至是饱和度或亮度完全不同的颜色。每种颜色都与一种颜色交换,该颜色具有相同的饱和度和亮度,但有一种新的基色。
    交换颜色的信息存储在 vec3 的数组中。 . x组件包含原始颜色的色调, y组件包含交换颜色的色调,以及 z组件包含一个 epsilon 值,它定义了颜色范围。

    着色器源文件应放在项目目录的“资源/着色器”子目录中。

    顶点着色器着色器/colorswap.vert
    attribute vec4 a_position;
    attribute vec2 a_texCoord;
    attribute vec4 a_color;

    varying vec4 cc_FragColor;
    varying vec2 cc_FragTexCoord1;

    void main()
    {
    gl_Position = CC_PMatrix * a_position;
    cc_FragColor = a_color;
    cc_FragTexCoord1 = a_texCoord;
    }

    片段着色器着色器/colorswap.frag
    #ifdef GL_ES
    precision mediump float;
    #endif

    varying vec4 cc_FragColor;
    varying vec2 cc_FragTexCoord1;

    const float Epsilon = 1e-10;

    vec3 RGBtoHCV( in vec3 RGB )
    {
    vec4 P = (RGB.g < RGB.b) ? vec4(RGB.bg, -1.0, 2.0/3.0) : vec4(RGB.gb, 0.0, -1.0/3.0);
    vec4 Q = (RGB.r < P.x) ? vec4(P.xyw, RGB.r) : vec4(RGB.r, P.yzx);
    float C = Q.x - min(Q.w, Q.y);
    float H = abs((Q.w - Q.y) / (6.0 * C + Epsilon) + Q.z);
    return vec3(H, C, Q.x);
    }

    vec3 RGBtoHSV(in vec3 RGB)
    {
    vec3 HCV = RGBtoHCV(RGB);
    float S = HCV.y / (HCV.z + Epsilon);
    return vec3(HCV.x, S, HCV.z);
    }

    vec3 HUEtoRGB(in float H)
    {
    float R = abs(H * 6.0 - 3.0) - 1.0;
    float G = 2.0 - abs(H * 6.0 - 2.0);
    float B = 2.0 - abs(H * 6.0 - 4.0);
    return clamp( vec3(R,G,B), 0.0, 1.0 );
    }

    vec3 HSVtoRGB(in vec3 HSV)
    {
    vec3 RGB = HUEtoRGB(HSV.x);
    return ((RGB - 1.0) * HSV.y + 1.0) * HSV.z;
    }

    #define MAX_SWAP 10
    uniform vec3 u_swap[MAX_SWAP];
    uniform int u_noSwap;

    void main()
    {
    vec4 originalColor = texture2D(CC_Texture0, cc_FragTexCoord1);
    vec3 originalHSV = RGBtoHSV( originalColor.rgb );
    vec4 swapColor = vec4( originalColor.rgb, 1.0 );

    for ( int i = 0; i < 10 ; ++ i )
    {
    if ( i >= u_noSwap )
    break;
    if ( abs( originalHSV.x - u_swap[i].x ) < u_swap[i].z )
    {
    swapColor.rgb = HSVtoRGB( vec3( u_swap[i].y, originalHSV.y, originalHSV.z ) );
    break;
    }
    }

    vec3 finalColor = mix( originalColor.rgb, swapColor.rgb, swapColor.a );
    gl_FragColor = vec4( finalColor.rgb, originalColor.a );
    }

    头文件 HelloWorldScene.h:
    #ifndef __HELLOWORLD_SCENE_H__
    #define __HELLOWORLD_SCENE_H__

    #include "cocos2d.h"

    #define MAX_COLOR 10

    class HelloWorld : public cocos2d::Scene
    {
    public:
    virtual bool init() override;
    static cocos2d::Scene* scene();
    void menuCloseCallback(Ref* sender);
    CREATE_FUNC(HelloWorld);
    void InitSwapInfo( int i, const cocos2d::Color3B &sourceCol, const cocos2d::Color3B &swapCol, float deviation );
    private:
    cocos2d::GLProgram* mProgramExample;
    cocos2d::Vec3 mSource[MAX_COLOR];
    cocos2d::Vec3 mSwap[MAX_COLOR];
    float mDeviation[MAX_COLOR];
    cocos2d::Vec3 mSwapInfo[MAX_COLOR];
    };

    #endif // __HELLOWORLD_SCENE_H__

    源文件 HelloWorldScene.cpp:

    注意,C++ 函数 RGBtoHue和 GLSL 函数 RGBtoHue ,应该实现完全相同的算法。
    函数的输入 SwapInfo RGB 颜色编码为 cocos2d::Vec3 .如果 RGB 颜色的源 channel 是字节( unsigned char ),那么这可以很容易地转换为 cocos2d::Vec3来自 cocos2d::Vec3( R / 255.0f, G / 255.0f, B / 255.0f ) .
    #include "HelloWorldScene.h"
    #include "AppMacros.h"

    USING_NS_CC;

    float RGBtoHue( const cocos2d::Vec3 &RGB )
    {
    const float Epsilon = 1e-10f;
    cocos2d::Vec4 P = (RGB.y < RGB.z) ?
    cocos2d::Vec4(RGB.y, RGB.z, -1.0f, 2.0f/3.0f) :
    cocos2d::Vec4(RGB.y, RGB.z, 0.0f, -1.0f/3.0f);
    cocos2d::Vec4 Q = (RGB.x < P.x) ?
    cocos2d::Vec4(P.x, P.y, P.w, RGB.x) :
    cocos2d::Vec4(RGB.x, P.y, P.z, P.x);
    float C = Q.x - (Q.w < Q.y ? Q.w : Q.y);
    float H = fabs((Q.w - Q.y) / (6.0f * C + Epsilon) + Q.z);
    return H;
    }

    cocos2d::Vec3 SwapInfo( const cocos2d::Vec3 &sourceCol, const cocos2d::Vec3 &swapCol, float epsi )
    {
    return cocos2d::Vec3( RGBtoHue( sourceCol ), RGBtoHue( swapCol ), epsi );
    }

    void HelloWorld::InitSwapInfo( int i, const cocos2d::Color3B &sourceCol, const cocos2d::Color3B &swapCol, float deviation )
    {
    mSource[i] = cocos2d::Vec3( sourceCol.r/255.0, sourceCol.g/255.0, sourceCol.b/255.0 );
    mSwap[i] = cocos2d::Vec3( swapCol.r/255.0, swapCol.g/255.0, swapCol.b/255.0 );
    mDeviation[i] = deviation;
    mSwapInfo[i] = SwapInfo( mSource[i], mSwap[i], mDeviation[i] );
    }

    Scene* HelloWorld::scene()
    {
    return HelloWorld::create();
    }

    bool HelloWorld::init()
    {
    if ( !Scene::init() ) return false;
    auto visibleSize = Director::getInstance()->getVisibleSize();
    auto origin = Director::getInstance()->getVisibleOrigin();

    auto closeItem = MenuItemImage::create(
    "CloseNormal.png",
    "CloseSelected.png",
    CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));

    closeItem->setPosition(origin + Vec2(visibleSize) - Vec2(closeItem->getContentSize() / 2));

    auto menu = Menu::create(closeItem, nullptr);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);

    auto sprite = Sprite::create("HelloWorld.png");
    sprite->setPosition(Vec2(visibleSize / 2) + origin);

    mProgramExample = new GLProgram();
    mProgramExample->initWithFilenames("shader/colorswap.vert", "shader/colorswap.frag");
    mProgramExample->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION);
    mProgramExample->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_COLOR);
    mProgramExample->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORDS);
    mProgramExample->link();
    mProgramExample->updateUniforms();
    mProgramExample->use();

    GLProgramState* state = GLProgramState::getOrCreateWithGLProgram(mProgramExample);
    sprite->setGLProgram(mProgramExample);
    sprite->setGLProgramState(state);

    InitSwapInfo( 0, cocos2d::Color3B( 41, 201, 226 ), cocos2d::Color3B( 255, 0, 0 ), 0.1f );
    InitSwapInfo( 1, cocos2d::Color3B( 249, 6, 6 ), cocos2d::Color3B( 255, 255, 0 ), 0.1f );
    int noOfColors = 2;
    state->setUniformVec3v("u_swap", noOfColors, mSwapInfo);
    state->setUniformInt("u_noSwap", noOfColors);

    this->addChild(sprite);

    return true;
    }

    void HelloWorld::menuCloseCallback(Ref* sender)
    {
    Director::getInstance()->end();

    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
    #endif
    }

    比较 RGB 值而不是色相

    直接比较 RGB 颜色的片段着色器如下所示:
    #ifdef GL_ES
    precision mediump float;
    #endif

    varying vec4 cc_FragColor;
    varying vec2 cc_FragTexCoord1;

    const float Epsilon = 1e-10;

    vec3 RGBtoHCV( in vec3 RGB )
    {
    vec4 P = (RGB.g < RGB.b) ? vec4(RGB.bg, -1.0, 2.0/3.0) : vec4(RGB.gb, 0.0, -1.0/3.0);
    vec4 Q = (RGB.r < P.x) ? vec4(P.xyw, RGB.r) : vec4(RGB.r, P.yzx);
    float C = Q.x - min(Q.w, Q.y);
    float H = abs((Q.w - Q.y) / (6.0 * C + Epsilon) + Q.z);
    return vec3(H, C, Q.x);
    }

    vec3 RGBtoHSV(in vec3 RGB)
    {
    vec3 HCV = RGBtoHCV(RGB);
    float S = HCV.y / (HCV.z + Epsilon);
    return vec3(HCV.x, S, HCV.z);
    }

    vec3 HUEtoRGB(in float H)
    {
    float R = abs(H * 6.0 - 3.0) - 1.0;
    float G = 2.0 - abs(H * 6.0 - 2.0);
    float B = 2.0 - abs(H * 6.0 - 4.0);
    return clamp( vec3(R,G,B), 0.0, 1.0 );
    }

    vec3 HSVtoRGB(in vec3 HSV)
    {
    vec3 RGB = HUEtoRGB(HSV.x);
    return ((RGB - 1.0) * HSV.y + 1.0) * HSV.z;
    }

    #define MAX_SWAP 10
    uniform vec3 u_orig[MAX_SWAP];
    uniform vec3 u_swap[MAX_SWAP];
    uniform float u_deviation[MAX_SWAP];
    uniform int u_noSwap;

    void main()
    {
    vec4 originalColor = texture2D(CC_Texture0, cc_FragTexCoord1);
    vec3 originalHSV = RGBtoHSV( originalColor.rgb );
    vec4 swapColor = vec4( originalColor.rgb, 1.0 );

    for ( int i = 0; i < 10 ; ++ i )
    {
    if ( i >= u_noSwap )
    break;
    if ( all( lessThanEqual( abs(originalColor.rgb - u_orig[i]), vec3(u_deviation[i]) ) ) )
    {
    vec3 swapHSV = RGBtoHSV( u_swap[i].rgb );
    swapColor.rgb = HSVtoRGB( vec3( swapHSV.x, originalHSV.y, originalHSV.z ) );
    break;
    }
    }

    vec3 finalColor = mix( originalColor.rgb, swapColor.rgb, swapColor.a );
    gl_FragColor = vec4( finalColor.rgb, originalColor.a );
    }

    注意,制服的初始化必须适应:
    int noOfColors = 2;
    state->setUniformVec3v("u_orig", noOfColors, mSource);
    state->setUniformVec3v("u_swap", noOfColors, mSwap);
    state->setUniformFloatv("u_deviation", noOfColors, mDeviation);
    state->setUniformInt("u_noSwap", noOfColors);

    答案的延伸

    如果应该交换确切指定的颜色,则着色器可以更加简化。为此,偏差 u_deviation必须受到限制(例如 deviation = 0.02; )。
    #ifdef GL_ES
    precision mediump float;
    #endif

    varying vec4 cc_FragColor;
    varying vec2 cc_FragTexCoord1;

    #define MAX_SWAP 11
    uniform vec3 u_orig[MAX_SWAP];
    uniform vec3 u_swap[MAX_SWAP];
    uniform float u_deviation[MAX_SWAP];
    uniform int u_noSwap;

    void main()
    {
    vec4 originalColor = texture2D(CC_Texture0, cc_FragTexCoord1);
    vec4 swapColor = vec4( originalColor.rgb, 1.0 );

    for ( int i = 0; i < MAX_SWAP ; ++ i )
    {
    vec3 deltaCol = abs( originalColor.rgb - u_orig[i] );
    float hit = step( deltaCol.x + deltaCol.y + deltaCol.z, u_deviation[i] * 3.0 );
    swapColor.rgb = mix( swapColor.rgb, u_swap[i].rgb, hit );
    }

    gl_FragColor = vec4( swapColor.rgb, originalColor.a );
    }

    如果源纹理中的每种颜色都有一个单独的颜色 channel (这意味着颜色值仅用于这种特殊颜色,例如红色 channel ),那么着色器代码可以进一步简化,因为只需要比较一个 channel :
    void main()
    {
    vec4 originalColor = texture2D(CC_Texture0, cc_FragTexCoord1);
    vec4 swapColor = vec4( originalColor.rgb, 1.0 );

    for ( int i = 0; i < MAX_SWAP ; ++ i )
    {
    float hit = step( abs( originalColor.r - u_orig[i].r ), u_deviation[i] );
    swapColor.rgb = mix( swapColor.rgb, u_swap[i].rgb, hit );
    }

    gl_FragColor = vec4( swapColor.rgb, originalColor.a );
    }

    进一步的优化将使我们回到本答案中描述的第一个算法。这种算法的一大优点是,每种颜色都被交换(除了交换纹理的 alpha channel 为 0),但在着色器中不必在查找表中进行昂贵的搜索。
    每种颜色将根据其红色 channel 由相应的颜色交换。如前所述,如果不应该交换颜色,则交换纹理的 alpha channel 必须设置为 0。

    新成员(member) mSwapTexture必须添加到类中:
    cocos2d::Texture2D* mSwapTexture;

    纹理可以很容易地创建,统一纹理采样器可以这样设置:
    #include <array>

    .....

    std::array< unsigned char, 256 * 4 > swapPlane{ 0 };
    for ( int c = 0; c < noOfColors; ++ c )
    {
    size_t i = (size_t)( mSource[c].x * 255.0 ) * 4;
    swapPlane[i+0] = (unsigned char)(mSwap[c].x*255.0);
    swapPlane[i+1] = (unsigned char)(mSwap[c].y*255.0);
    swapPlane[i+2] = (unsigned char)(mSwap[c].z*255.0);
    swapPlane[i+3] = 255;
    }
    mSwapTexture = new Texture2D();
    mSwapTexture->setAliasTexParameters();
    cocos2d::Size contentSize;
    mSwapTexture->initWithData( swapPlane.data(), swapPlane.size(), Texture2D::PixelFormat::RGBA8888, 256, 1, contentSize );
    state->setUniformTexture( "u_swapTexture", mSwapTexture );

    片段着色器如下所示:
    #ifdef GL_ES
    precision mediump float;
    #endif

    varying vec4 cc_FragColor;
    varying vec2 cc_FragTexCoord1;

    uniform sampler2D u_swapTexture; // lookup texture with 256 swap colors

    void main()
    {
    vec4 originalColor = texture2D(CC_Texture0, cc_FragTexCoord1);
    vec4 swapColor = texture2D(u_swapTexture, vec2(originalColor.r, 0.0));
    vec3 finalColor = mix(originalColor.rgb, swapColor.rgb, swapColor.a);
    gl_FragColor = vec4(finalColor.rgb, originalColor.a);
    }

    当然,查找键不一定是红色 channel ,任何其他 channel 也是可以的。
    通过使用增加的二维查找纹理,甚至可以组合 2 个颜色 channel 。请参阅以下示例,该示例演示如何使用具有 1024 个条目的查找纹理。查找表使用 X 维度中的完整红色 channel (256 个索引)和 Y 维度中的绿色 channel 除以 64(4 个索引)。

    创建一个二维查找表:
    std::array< unsigned char, 1024 * 4 > swapPlane{ 0 };
    for ( int c = 0; c < noOfColors; ++ c )
    {
    size_t ix = (size_t)( mSource[c].x * 255.0 );
    size_t iy = (size_t)( mSource[c].y * 255.0 / 64.0 );
    size_t i = ( iy * 256 + ix ) * 4;
    swapPlane[i+0] = (unsigned char)(mSwap[c].x*255.0);
    swapPlane[i+1] = (unsigned char)(mSwap[c].y*255.0);
    swapPlane[i+2] = (unsigned char)(mSwap[c].z*255.0);
    swapPlane[i+3] = 255;
    }
    mSwapTexture = new Texture2D();
    mSwapTexture->setAliasTexParameters();
    cocos2d::Size contentSize;
    mSwapTexture->initWithData( swapPlane.data(), swapPlane.size(), Texture2D::PixelFormat::RGBA8888, 256, 4, contentSize );

    并调整片段着色器:
    void main()
    {
    vec4 originalColor = texture2D(CC_Texture0, cc_FragTexCoord1);
    vec4 swapColor = texture2D(u_swapTexture, originalColor.rg);
    vec3 finalColor = mix(originalColor.rgb, swapColor.rgb, swapColor.a);
    gl_FragColor = vec4(finalColor.rgb, originalColor.a);
    }

    插入纹理

    由于无法使用 GL_LINEAR使用上述方法,如果需要,必须模拟:
    #ifdef GL_ES
    precision mediump float;
    #endif

    varying vec4 cc_FragColor;
    varying vec2 cc_FragTexCoord1;

    uniform sampler2D u_swapTexture; // lookup texture with 256 swap colors
    uniform vec2 u_spriteSize;

    void main()
    {
    vec2 texS = 1.0 / u_spriteSize;
    vec2 texF = fract( cc_FragTexCoord1 * u_spriteSize + 0.5 );
    vec2 texC = (cc_FragTexCoord1 * u_spriteSize + 0.5 - texF) / u_spriteSize;

    vec4 originalColor = texture2D(CC_Texture0, texC);
    vec4 swapColor = texture2D(u_swapTexture, originalColor.rg);
    vec3 finalColor00 = mix(originalColor.rgb, swapColor.rgb, swapColor.a);

    originalColor = texture2D(CC_Texture0, texC+vec2(texS.x, 0.0));
    swapColor = texture2D(u_swapTexture, originalColor.rg);
    vec3 finalColor10 = mix(originalColor.rgb, swapColor.rgb, swapColor.a);

    originalColor = texture2D(CC_Texture0, texC+vec2(0.0,texS.y));
    swapColor = texture2D(u_swapTexture, originalColor.rg);
    vec3 finalColor01 = mix(originalColor.rgb, swapColor.rgb, swapColor.a);

    originalColor = texture2D(CC_Texture0, texC+texS.xy);
    swapColor = texture2D(u_swapTexture, originalColor.rg);
    vec3 finalColor11 = mix(originalColor.rgb, swapColor.rgb, swapColor.a);

    vec3 finalColor0 = mix( finalColor00, finalColor10, texF.x );
    vec3 finalColor1 = mix( finalColor01, finalColor11, texF.x );
    vec3 finalColor = mix( finalColor0, finalColor1, texF.y );

    gl_FragColor = vec4(finalColor.rgb, originalColor.a);
    }

    新的统一变量 u_spriteSize必须像这样设置:
    auto size = sprite->getTexture()->getContentSizeInPixels();
    state->setUniformVec2( "u_spriteSize", Vec2( (float)size.width, (float)size.height ) );

    修改CPU上的纹理

    当然,也可以在 CPU 上修改纹理,但是对于每组交换颜色,必须生成一个单独的纹理。优点是不再需要着色器。
    以下代码在加载纹理时交换颜色。必须完全跳过着色器。
    Sprite * sprite = nullptr;

    std::string imageFile = ....;
    std::string fullpath = FileUtils::getInstance()->fullPathForFilename(imageFile);
    cocos2d::Image *img = !fullpath.empty() ? new Image() : nullptr;
    if (img != nullptr && img->initWithImageFile(fullpath))
    {
    if ( img->getRenderFormat() == Texture2D::PixelFormat::RGBA8888 )
    {
    unsigned char *plane = img->getData();
    for ( int y = 0; y < img->getHeight(); ++ y )
    {
    for ( int x = 0; x < img->getWidth(); ++ x )
    {
    size_t i = ( y * img->getWidth() + x ) * 4;
    unsigned char t = plane[i];
    for ( int c = 0; c < noOfColors; ++ c )
    {
    if ( fabs(mSource[c].x - plane[i+0]/255.0f) < mDeviation[c] &&
    fabs(mSource[c].y - plane[i+1]/255.0f) < mDeviation[c] &&
    fabs(mSource[c].z - plane[i+2]/255.0f) < mDeviation[c] )
    {
    plane[i+0] = (unsigned char)(mSwap[c].x*255.0);
    plane[i+1] = (unsigned char)(mSwap[c].y*255.0);
    plane[i+2] = (unsigned char)(mSwap[c].z*255.0);
    }
    }
    }
    }
    }

    std::string key = "my_swap_" + imageFile;
    if ( Texture2D *texture = _director->getTextureCache()->addImage( img, key ) )
    sprite = Sprite::createWithTexture( texture );
    }

    CPU 和 GPU 的组合方法

    如果总是交换纹理的相同区域(颜色),则可以使用此方法。这种方法的优点是,原始纹理只修改一次,但纹理的每个应用程序都可以拥有自己的交换表。
    对于这种方法,alpha channel 用于保存交换颜色的索引。在下面的示例代码中,从 1 到包括 11 的值范围用于存储交换颜色的索引。 0 保留用于绝对透明。
    Sprite * sprite = nullptr;

    std::string imageFile = ....;
    std::string key = "my_swap_" + imageFile;
    Texture2D *texture = _director->getTextureCache()->getTextureForKey( key );
    if (texture == nullptr)
    {
    std::string fullpath = FileUtils::getInstance()->fullPathForFilename(imageFile);
    cocos2d::Image *img = !fullpath.empty() ? new Image() : nullptr;
    if ( img->initWithImageFile(fullpath) &&
    img->getRenderFormat() == Texture2D::PixelFormat::RGBA8888 )
    {
    unsigned char *plane = img->getData();
    for ( int y = 0; y < img->getHeight(); ++ y )
    {
    for ( int x = 0; x < img->getWidth(); ++ x )
    {
    size_t i = ( y * img->getWidth() + x ) * 4;
    unsigned char t = plane[i];
    for ( int c = 0; c < noOfColors; ++ c )
    {
    if ( fabs(mSource[c].x - plane[i+0]/255.0f) < mDeviation[c] &&
    fabs(mSource[c].y - plane[i+1]/255.0f) < mDeviation[c] &&
    fabs(mSource[c].z - plane[i+2]/255.0f) < mDeviation[c] )
    {
    plane[i+3] = (unsigned char)(c+1);
    }
    }
    }
    }
    texture = _director->getTextureCache()->addImage( img, key );
    }
    }
    if ( texture != nullptr )
    sprite = Sprite::createWithTexture( texture );

    片段着色器只需要制服 u_swapu_noSwap并且不必进行昂贵的搜索。
    #ifdef GL_ES
    precision mediump float;
    #endif

    varying vec4 cc_FragColor;
    varying vec2 cc_FragTexCoord1;

    #define MAX_SWAP 11
    uniform vec3 u_swap[MAX_SWAP];
    uniform int u_noSwap;

    void main()
    {
    vec4 originalColor = texture2D(CC_Texture0, cc_FragTexCoord1);
    float fIndex = originalColor.a * 255.0 - 0.5;
    float maxIndex = float(u_noSwap) + 0.5;
    int iIndex = int( clamp( fIndex, 0.0, maxIndex ) );
    float isSwap = step( 0.0, fIndex ) * step( fIndex, maxIndex );
    vec3 swapColor = mix( originalColor.rgb, u_swap[iIndex], isSwap );
    gl_FragColor = vec4( swapColor.rgb, max(originalColor.a, isSwap) );
    }

    关于c++ - 即时重新着色 Sprite ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45822889/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com