GLSL 卡通shader

来源:互联网 发布:mac怎么看央视直播 编辑:程序博客网 时间:2024/04/25 04:13
浏览Geeks3D,发现一个好玩儿的shader,改了个gles版本,用cocos2dx在iphone上跑了一下,效果如图:

原文shader是gl版,在手机上跑的很慢,iphone5全屏处理情况下帧数仅为10帧左右,我做了一些优化,使得全屏情况下帧数提升到30左右。

shader我加了些注释,如下:

#ifdef GL_ES
precision mediump float;
#endif

varying vec2 v_texCoord;
varying vec4 v_fragmentColor;

uniform float u_texWidth;
uniform float u_texHeight;
uniform float touch_x_offset;

const float edge_thres = 0.2;
const float edge_thres2 = 5.0;

#define HueLevCount 6
#define SatLevCount 7
#define ValLevCount 4
//HSV区间
float HueLevels[HueLevCount];
float SatLevels[SatLevCount];
float ValLevels[ValLevCount];

//H(色调)范围(0-360)分为6份
void buildHueLevel()
{
    HueLevels[0]= 0.0;
    HueLevels[1]= 140.0;
    HueLevels[2]= 160.0;
    HueLevels[3]= 240.0;
    HueLevels[4]= 240.0;
    HueLevels[5]= 360.0;
}

//S(饱和度)范围(0-1.0)分为7份
void buildSatLevel()
{
    SatLevels[0]= 0.0;
    SatLevels[1]= 0.15;
    SatLevels[2]= 0.3;
    SatLevels[3]= 0.45;
    SatLevels[4]= 0.6;
    SatLevels[5]= 0.8;
    SatLevels[6]= 1.0;
}

//V(亮度)范围(0-1.0)分为4份
void buildValLevel()
{
    ValLevels[0]= 0.0;
    ValLevels[1]= 0.3;
    ValLevels[2]= 0.6;
    ValLevels[3]= 1.0;
}

//将RGB转为HSV
vec3 RGBtoHSV(float r, float g, float b)
{
    float minv,maxv, delta;
    vec3res;
   
    minv =min(min(r, g), b);
    maxv =max(max(r, g), b);
    res.z =maxv;           // v
   
    delta = maxv- minv;
   
    if( maxv !=0.0 )
       res.y = delta /maxv;     // s
    else {
       // r = g = b =0     // s = 0, v is undefined
       res.y = 0.0;
       res.x = -1.0;
       return res;
    }
   
    if( r ==maxv )
       res.x = ( g - b ) /delta;     // between yellow & magenta
    else if( g== maxv )
       res.x = 2.0 + ( b - r ) /delta;   // between cyan &yellow
    else
       res.x = 4.0 + ( r - g ) /delta;   // between magenta &cyan
   
    res.x =res.x *60.0;           // degrees
    if( res.x< 0.0 )
       res.x = res.x + 360.0;
   
    returnres;
}

//HSV转RGB
vec3 HSVtoRGB(float h, float s, float v)
{
    int i;
    float f, p,q, t;
    vec3res;
   
    if( s == 0.0)
    {
       // achromatic (grey)
       res.x = v;
       res.y = v;
       res.z = v;
       return res;
    }
   
    h /=60.0;        // sector 0 to 5
    i =int(floor( h ));
    f = h -float(i);        // factorial part of h
    p = v * (1.0 - s );
    q = v * (1.0 - s * f );
    t = v * (1.0 - s * ( 1.0 - f ) );
   
    if(i == 0){
       res.x = v;
       res.y = t;
       res.z = p;
    }
    else if(i ==1) {
       res.x = q;
       res.y = v;
       res.z = p;
    }
    else if(i ==2) {
       res.x = p;
       res.y = v;
       res.z = t;
    }
    else if(i ==3) {
       res.x = p;
       res.y = q;
       res.z = v;
    }
    else if(i ==4) {
       res.x = t;
       res.y = p;
       res.z = v;
    }
    else {
       res.x = v;
       res.y = p;
       res.z = q;
    }

    returnres;
}

//根据参数值所在区间返回H值
float nearestHueLevel(float col)
{
    if(col <HueLevels[2]) {
       if(col >= HueLevels[1]) {
           return HueLevels[2];
       }

       return HueLevels[1];
    }
    else if(col> HueLevels[3]) {
       if(col <= HueLevels[4]) {
           return HueLevels[4];
       }
       
       return HueLevels[5];
    }
   
    returnHueLevels[3];
}

float nearestSatLevel(float col)
{
    if(col <=SatLevels[3]) {
       if(col > SatLevels[2])
           return SatLevels[3];
       else if(col > SatLevels[1]) {
               return SatLevels[2];
       }
       
       return SatLevels[1];
    }
    else {
       if(col <= SatLevels[5]) {
           if(col > SatLevels[4])
               return SatLevels[5];
           return SatLevels[4];
       }
       
       return SatLevels[6];
    }
}

float nearestValLevel(float col)
{
    if(col >=ValLevels[1] && col <= ValLevels[2]) {
       return ValLevels[2];
    }
    else if(col<= ValLevels[1]) {
       return ValLevels[1];
    }
   
    returnValLevels[3];
}

// averaged pixel intensity from 3 color channels
float avg_intensity(vec4 pix)
{
    return(pix.r + pix.g + pix.b)/3.0;
}

vec4 get_pixel(vec2 coords, float dx, float dy)
{
    returntexture2D(CC_Texture0,coords + vec2(dx, dy));
}

//边缘检测(参照链接)
float IsEdge(in vec2 coords)
{
    float dxtex= 1.0 /u_texWidth;
    float dytex= 1.0 /u_texHeight;
    floatpix[9];
    floatdelta;
   
    // readneighboring pixel intensities
    pix[0] =avg_intensity(get_pixel(coords, -dxtex, -dytex));
    pix[1] =avg_intensity(get_pixel(coords, -dxtex, 0.));
    pix[2] =avg_intensity(get_pixel(coords, -dxtex, dytex));
    pix[3] =avg_intensity(get_pixel(coords, 0.,   -dytex));
    pix[4] =avg_intensity(get_pixel(coords, 0.,    0.));
    pix[5] =avg_intensity(get_pixel(coords, 0.,    dytex));
    pix[6] =avg_intensity(get_pixel(coords,  dxtex,-dytex));
    pix[7] =avg_intensity(get_pixel(coords, dxtex,  0.));
    pix[8] =avg_intensity(get_pixel(coords, dxtex,  dytex));
   
    // averagecolor differences around neighboring pixels
    delta =(abs(pix[1]-pix[7])+
            abs(pix[5]-pix[3])+
            abs(pix[0]-pix[8])+
            abs(pix[2]-pix[6]))*0.25;
   
    //returnclamp(5.0*delta,0.0,1.0);
    returnclamp(edge_thres2*delta,0.0,1.0);
}

void main()
{
   buildHueLevel();
   buildSatLevel();
   buildValLevel();
   
    vec2 uv =v_texCoord.xy;
    vec4 tc =vec4(1.0, 0.0, 0.0, 1.0);
   
    if (uv.x> (touch_x_offset+0.002)) {
       vec3 colorOrg = texture2D(CC_Texture0, uv).rgb;
       //将RGB转化到HSV颜色空间
       vec3 vHSV = RGBtoHSV(colorOrg.r,colorOrg.g,colorOrg.b);
       //将HSV值变换到预先定义的区间值中
       vHSV.x = nearestHueLevel(vHSV.x);
       vHSV.y = nearestSatLevel(vHSV.y);
       vHSV.z = nearestValLevel(vHSV.z);
       
       //边缘检测
       float edg = IsEdge(uv);
       //边框为黑色,否则将HSV转回RGB
       vec3 vRGB = (edg >= edge_thres)?vec3(0.0,0.0,0.0):HSVtoRGB(vHSV.x,vHSV.y,vHSV.z);

       tc = vec4(vRGB.x,vRGB.y,vRGB.z, 1);
    }
    else if(uv.x < (touch_x_offset-0.002)) {
       tc = texture2D(CC_Texture0, uv);
    }

    gl_FragColor= tc * v_fragmentColor;
}

HSV相关概念可以google。

cocos2dx代码如下:
TToonifyTest.h

#include "cocos2d.h"
USING_NS_CC;

class TToonifyTest : public Layer
{
public:
    staticcocos2d::Scene* createScene();
   
   CREATE_FUNC(TToonifyTest);
   
    virtual boolinit() override;
   
    virtual boolonTouchBegan(Touch *touch, Event *unused_event) override;
    virtual voidonTouchMoved(Touch *touch, Event *unused_event) override;
   
    voidrefreshRatio(const Vec2& pos);
   
protected:
    Sprite*m_pSpToonify;
   
};

TToonifyTest.cpp

#include "TToonifyTest.h"

cocos2d::Scene* TToonifyTest::createScene()
{
    // 'scene'is an autorelease object
    auto scene =Scene::create();
   
    // 'layer'is an autorelease object
    auto layer =TToonifyTest::create();
   
    // add layeras a child to scene
   scene->addChild(layer);
   
    // returnthe scene
    returnscene;
}

bool TToonifyTest::init()
{
   if(!Layer::init()) return false;
   
    m_pSpToonify= Sprite::create("test.jpg");
   m_pSpToonify->setAnchorPoint(Vec2(0, 0));
   m_pSpToonify->setPosition(Vec2(0, 30));
   this->addChild(m_pSpToonify, 1);
   
    auto strData=FileUtils::getInstance()->getStringFromFile("Toonify.fsh");
    auto program= GLProgram::createWithByteArrays(ccPositionTextureColor_vert,(const GLchar*)strData.c_str());
   m_pSpToonify->setGLProgram(program);
   
   m_pSpToonify->getGLProgramState()->setUniformFloat("u_texWidth",m_pSpToonify->getTexture()->getContentSize().width);
   m_pSpToonify->getGLProgramState()->setUniformFloat("u_texHeight",m_pSpToonify->getTexture()->getContentSize().height);
   m_pSpToonify->getGLProgramState()->setUniformFloat("touch_x_offset",0.5);
   
    autolistener = EventListenerTouchOneByOne::create();
   listener->onTouchBegan =CC_CALLBACK_2(TToonifyTest::onTouchBegan, this);
   listener->onTouchMoved =CC_CALLBACK_2(TToonifyTest::onTouchMoved, this);
   _eventDispatcher->addEventListenerWithSceneGraphPriority(listener,this);
   
    returntrue;
}

bool TToonifyTest::onTouchBegan(Touch *touch, Event*unused_event)
{
   refreshRatio(this->convertTouchToNodeSpace(touch));
   
    returntrue;
}

void TToonifyTest::onTouchMoved(Touch *touch, Event*unused_event)
{
   refreshRatio(this->convertTouchToNodeSpace(touch));
}

void TToonifyTest::refreshRatio(const Vec2& pos)
{
    float f =pos.x /m_pSpToonify->getTexture()->getContentSize().width;
    if(f <0.01) f = 0.01;
    if(f >0.99) f = 0.99;
   
   m_pSpToonify->getGLProgramState()->setUniformFloat("touch_x_offset",f);
}

cocos2dx版本:3.1.1
测试环境:iphone5

参考链接
原文:http://www.geeks3d.com/20140523/glsl-shader-library-toonify-post-processing-filter/
边缘检测相关:http://rastergrid.com/blog/2011/01/frei-chen-edge-detector/

PS:懒+忙 导致我几个月都没写博,前阵子还大病一场,恢复中。

0 0
原创粉丝点击