KAM*_*AZE 9 c++ glsl cocos2d-x
我有很多相同的图形但不同的颜色.我想通过从灰度图像着色来优化它.此外,我想在实时精灵对象的游戏过程中,在飞行中改变它的颜色.还逐渐将颜色值从一种颜色类型更改为另一种颜色类型
要调色灰度精灵,可以通过简单的片段着色器来完成,该着色器将纹理的纹理元素的颜色与色调颜色相乘.这导致通过灰度纹理在亮度中增加恒定的颜色.
以下所有着色器都考虑了Premultiplied Alpha.
顶点着色器着色器/ tone.vert
attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 cc_FragTexCoord1;
void main()
{
gl_Position = CC_PMatrix * a_position;
cc_FragTexCoord1 = a_texCoord;
}
Run Code Online (Sandbox Code Playgroud)
片段着色器着色器/ tone.frag
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 cc_FragTexCoord1;
uniform vec3 u_tintColor;
void main()
{
float normTint = 0.30 * u_tintColor.r + 0.59 * u_tintColor.g + 0.11 * u_tintColor.b;
vec4 texColor = texture2D( CC_Texture0, cc_FragTexCoord1 );
vec3 mixColor = u_tintColor * texColor / normTint;
gl_FragColor = vec4( mixColor.rgb, texColor.a );
}
Run Code Online (Sandbox Code Playgroud)
为着色器程序对象添加类成员:
cocos2d::GLProgram* mProgram;
Run Code Online (Sandbox Code Playgroud)
创建着色器程序,将其添加到精灵并在初始化期间设置制服:
auto sprite = cocos2d::Sprite::create( ..... );
sprite->setPosition( ..... );
mProgram = new cocos2d::GLProgram();
mProgram->initWithFilenames("shader/tone.vert", "shader/tone.frag");
mProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION);
mProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORDS);
mProgram->link();
mProgram->updateUniforms();
mProgram->use();
GLProgramState* state = GLProgramState::getOrCreateWithGLProgram(mProgram);
sprite->setGLProgram(mProgram);
sprite->setGLProgramState(state);
cocos2d::Color3B tintColor( 255, 255, 0 ); // e.g yellow
cocos2d::Vec3 tintVal( tintColor.r/255.0f, tintColor.g/255.0f, tintColor.b/255.0f );
state->setUniformVec3("u_tintColor", tintVal);
Run Code Online (Sandbox Code Playgroud)
如果你首先必须从RGB精灵创建灰度,然后你想要为精灵着色,那么你必须稍微调整片段着色器.
通常使用公式创建灰度颜色gray = 0.2126 * red + 0.7152 * green + 0.0722 * blue(在网上有不同的亮度公式和解释:Luma(视频),七种灰度转换算法.)根据距离,您可以在原始颜色和黑白颜色之间进行插值.
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 cc_FragTexCoord1;
uniform vec3 u_tintColor;
void main()
{
float normTint = 0.30 * u_tintColor.r + 0.59 * u_tintColor.g + 0.11 * u_tintColor.b;
vec4 texColor = texture2D( CC_Texture0, cc_FragTexCoord1 );
float gray = 0.30 * texColor.r + 0.59 * texColor.g + 0.11 * texColor.b;
vec3 mixColor = u_tintColor * gray / normTint;
gl_FragColor = vec4( mixColor.rgb, texColor.a );
}
Run Code Online (Sandbox Code Playgroud)
要进行从灰度到颜色的映射,也可以使用渐变纹理.请参阅以下片段着色器:
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 cc_FragTexCoord1;
uniform sampler2D u_texGrad;
void main()
{
vec4 texColor = texture2D( CC_Texture0, cc_FragTexCoord1 );
vec4 lookUpCol = texture2D( u_texGrad, vec2( texColor.r / max(texColor.a, 0.01), 0.0 ) );
float alpha = texColor.a * lookUpCol.a;
gl_FragColor = vec4( lookUpCol.rgb * alpha, alpha );
}
Run Code Online (Sandbox Code Playgroud)
要使用此着色器,必须添加2D纹理mebmer:
cocos2d::Texture2D* mGradinetTexture;
Run Code Online (Sandbox Code Playgroud)
纹理和制服必须设置如下:
std::string gradPath = FileUtils::getInstance()->fullPathForFilename("grad.png");
cocos2d::Image *gradImg = new Image();
gradImg->initWithImageFile( gradPath );
mGradinetTexture = new Texture2D();
mGradinetTexture->setAliasTexParameters();
mGradinetTexture->initWithImage( gradImg );
state->setUniformTexture("u_texGrad", mGradinetTexture);
Run Code Online (Sandbox Code Playgroud)
进一步的改进是自动调整颜色的梯度
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 cc_FragTexCoord1;
uniform sampler2D u_texGrad;
void main()
{
vec4 texColor = texture2D( CC_Texture0, cc_FragTexCoord1 );
vec4 lookUpCol = texture2D( u_texGrad, vec2( texColor.r / max(texColor.a, 0.01), 0.5 ) );
float lookUpGray = 0.30 * lookUpCol.r + 0.59 * lookUpCol.g + 0.11 * lookUpCol.b;
lookUpCol *= texColor.r / lookUpGray;
float alpha = texColor.a * lookUpCol.a;
gl_FragColor = vec4( lookUpCol.rgb * alpha, alpha );
}
Run Code Online (Sandbox Code Playgroud)
如果纹理的不透明部分和纹理的透明部分之间应该有一个硬过渡,那么设置片段颜色的着色器部分必须像这样调整:
float alpha = step( 0.5, texColor.a ) * lookUpCol.a;
gl_FragColor = vec4( lookUpCol.rgb * alpha, alpha );
Run Code Online (Sandbox Code Playgroud)
要通过一组颜色创建渐变纹理,我建议使用牛顿多项式.以下算法处理任意数量的颜色,这些颜色必须分布在渐变上.每种颜色都必须映射到灰度值,灰度值必须按升序设置.该算法必须至少设置2种颜色.
这意味着,例如,如果有颜色c0,c1并且c2,它对应于灰度值g0,g1并且g2算法必须像这样初始化:
g0 = 131
g1 = 176
g2 = 244
std::vector< cocos2d::Color3B > gradBase{ cg0, cg1, cg2 };
std::vector< float > x_val{ 131 / 255.0f, 176 / 255.0f, 244 / 255.0f };
std::vector< cocos2d::Color3B > gradBase{ cr0, cr1, cr2 };
std::vector< float > x_val{ 131 / 255.0f, 176 / 255.0f, 244 / 255.0f };
Run Code Online (Sandbox Code Playgroud)
C++代码:
unsigned char ClampColor( float colF )
{
int c = (int)(colF * 255.0f + 0.5f);
return (unsigned char)(c < 0 ? 0 : ( c > 255 ? 255 : c ));
}
Run Code Online (Sandbox Code Playgroud)
std::vector< cocos2d::Color3B > gradBase{ c0, c1, ..., cN };
std::vector< float > x_val{ g0, g1, ..., gn };
for ( int g = 0; g < x_val.size(); ++ g ) {
x_val[g] = x_val[g] / 255.0f;
}
x_val.push_back( 1.0f );
gradBase.push_back( Color3B( 255, 255, 255 ) );
std::vector< std::array< float, 3 > > alpha;
for ( int c = 0; c < (int)gradBase.size(); ++c )
{
std::array< float, 3 >alphaN{ gradBase[c].r / 255.0f, gradBase[c].g / 255.0f, gradBase[c].b / 255.0f };
for ( int i = 0; i < c; ++ i )
{
alphaN[0] = ( alphaN[0] - alpha[i][0] ) / (x_val[c]-x_val[i]);
alphaN[1] = ( alphaN[1] - alpha[i][1] ) / (x_val[c]-x_val[i]);
alphaN[2] = ( alphaN[2] - alpha[i][2] ) / (x_val[c]-x_val[i]);
}
alpha.push_back( alphaN );
}
std::array< unsigned char, 256 * 4 > gradPlane;
for ( int g = 0; g < 256; ++ g )
{
float x = g / 255.0;
std::array< float, 3 >col = alpha[0];
if ( x < x_val[0] )
{
col = { col[0]*x/x_val[0] , col[1]*x/x_val[0], col[2]*x/x_val[0] };
}
else
{
for ( int c = 1; c < (int)gradBase.size(); ++c )
{
float w = 1.0f;
for ( int i = 0; i < c; ++ i )
w *= x - x_val[i];
col = { col[0] + alpha[c][0] * w, col[1] + alpha[c][1] * w, col[2] + alpha[c][2] * w };
}
}
size_t i = g * 4;
gradPlane[i+0] = ClampColor(col[0]);
gradPlane[i+1] = ClampColor(col[1]);
gradPlane[i+2] = ClampColor(col[2]);
gradPlane[i+3] = 255;
}
Run Code Online (Sandbox Code Playgroud)
mGradinetTexture = new Texture2D();
cocos2d::Size contentSize;
mGradinetTexture->setAliasTexParameters();
mGradinetTexture->initWithData( gradPlane.data(), gradPlane.size() / 4, Texture2D::PixelFormat::RGBA8888, 256, 1, contentSize );
Run Code Online (Sandbox Code Playgroud)
注意,在这种情况下,当然必须使用没有自动调整的着色器,因为调整会使非线性梯度线性化.
这是从灰度颜色到RGB颜色的简单映射.映射表的左侧(灰度值)是常量,而表的右侧(RGB值)必须调整到纹理,必须从灰度纹理重新创建.优点是可以映射所有灰度值,因为生成了梯度映射纹理.
虽然映射表的颜色与源纹理完全匹配,但两者之间的颜色是内插的.
请注意,GL_NEAREST对于渐变纹理,必须将纹理过滤器参数设置为获得准确的结果.在cocos2d-x这可以通过Texture2D::setAliasTexParameters.
由于颜色通道被编码为一个字节(unsigned byte),插值算法可以简化,而不会有明显的质量损失,特别是如果有一些颜色超过3个
.以下算法对基点之间的颜色进行线性插值.从开始到第一点,存在从RGB颜色(0,0,0)到第一颜色的线性插值.最后(超过最后一个基点)保留最后的RGB颜色,以避免明亮的白色毛刺.
unsigned char ClampColor( float colF )
{
int c = (int)(colF * 255.0f + 0.5f);
return (unsigned char)(c < 0 ? 0 : ( c > 255 ? 255 : c ));
}
Run Code Online (Sandbox Code Playgroud)
std::vector< cocos2d::Color4B >gradBase {
Color4B( 129, 67, 73, 255 ),
Color4B( 144, 82, 84, 255 ),
Color4B( 161, 97, 95, 255 ),
Color4B( 178, 112, 105, 255 ),
Color4B( 195, 126, 116, 255 ),
Color4B( 211, 139, 127, 255 ),
Color4B( 219, 162, 133, 255 ),
Color4B( 228, 185, 141, 255 ),
Color4B( 235, 207, 149, 255 ),
Color4B( 245, 230, 158, 255 ),
Color4B( 251, 255, 166, 255 )
};
std::vector< float > x_val { 86, 101, 116, 131, 146, 159, 176, 193, 209, 227, 244 };
for ( int g = 0; g < x_val.size(); ++ g ) {
x_val[g] = x_val[g] / 255.0f;
}
Run Code Online (Sandbox Code Playgroud)
std::array< unsigned char, 256 * 4 > gradPlane;
size_t x_i = 0;
for ( int g = 0; g < 256; ++ g )
{
float x = g / 255.0;
if ( x_i < x_val.size()-1 && x >= x_val[x_i] )
++ x_i;
std::array< float, 4 > col;
if ( x_i == 0 )
{
std::array< float, 4 > col0{ gradBase[0].r / 255.0f, gradBase[0].g / 255.0f, gradBase[0].b / 255.0f, gradBase[0].a / 255.0f };
col = { col0[0]*x/x_val[0] , col0[1]*x/x_val[0], col0[2]*x/x_val[0], col0[3]*x/x_val[0] };
}
else if ( x_i == x_val.size() )
{
col = { gradBase.back().r / 255.0f, gradBase.back().g / 255.0f, gradBase.back().b / 255.0f, gradBase.back().a / 255.0f };
}
else
{
std::array< float, 4 > col0{ gradBase[x_i-1].r / 255.0f, gradBase[x_i-1].g / 255.0f, gradBase[x_i-1].b / 255.0f, gradBase[x_i-1].a / 255.0f };
std::array< float, 4 > col1{ gradBase[x_i].r / 255.0f, gradBase[x_i].g / 255.0f, gradBase[x_i].b / 255.0f, gradBase[x_i].a / 255.0f };
float a = (x - x_val[x_i-1]) / (x_val[x_i] - x_val[x_i-1]);
col = { col0[0] + (col1[0]-col0[0])*a, col0[1] + (col1[1]-col0[1])*a, col0[2] + (col1[2]-col0[2])*a, col0[3] + (col1[3]-col0[3])*a };
}
size_t i = g * 4;
gradPlane[i+0] = ClampColor(col[0]);
gradPlane[i+1] = ClampColor(col[1]);
gradPlane[i+2] = ClampColor(col[2]);
gradPlane[i+3] = ClampColor(col[3]);
}
Run Code Online (Sandbox Code Playgroud)