OpenGL


基础

GLSL

  1. 调试工具ShaderDesigner

    https://www.opengl.org/sdk/tools/ShaderDesigner/

  1. OpenGL着色语言(OpenGL Shading Language)

    http://www.ayqy.net/blog/glsl-es%EF%BC%88opengl-es%E7%9D%80%E8%89%B2%E5%99%A8%E8%AF%AD%E8%A8%80%EF%BC%89_webgl%E7%AC%94%E8%AE%B09/

    教程:http://blog.csdn.net/racehorse/article/details/6593719

    http://www.apkbus.com/blog-99192-39382.html

    http://www.apkbus.com/home.php?mod=space&uid=99192&do=blog&quickforward=1&id=39584

    http://blog.csdn.net/dingkun520wy/article/details/49893305

是用来在OpenGL中着色编程的语言,他们是在图形卡的GPU上执行的。比如:视图转换、投影转换等。

GLSL的着色器代码分成2个部分:

    Vertex Shader(顶点着色器)
    Fragment(片断着色器)
    有时还会有Geometry Shader(几何着色器)

GLSL其使用C语言作为基础高阶着色语言
  1. 顶点着色器(Vertex Shader)主要的工作

    • 在coco2dx中是vsh文件

      1. 利用视图和投影矩阵对点的位置进行变化
      2. 如果需要利用法线的时候,也同样需要利用视图矩阵对其进行转换
      3. 纹理坐标的产生和转换
      4. 顶点的光照或者象素光照的计算
      5. 颜色计算

        uniform vec3 lightposition;//光源位置
        uniform vec3 eyeposition;//相机位置
        uniform vec4 ambient;//环境光颜色
        uniform vec4 lightcolor;//光源颜色
        uniform float Ns;//高光系数
        uniform float attenuation;//光线的衰减系数

        varying vec4 color;//向片段着色其传递的参数

        void main()
        {
        /
        很多示例中都是利用uniform参数从应用程序中向shader里传递当前模型视图矩阵和模型视图投影矩阵,
        其实对于初学者来说,我们大可以先用GLSL的内建变量:gl_ModelViewMatrix和gl_ModelViewProjectionMatrix代替,
        而顶点坐标的变换则直接可以利用内建函数ftransform()实现。当然,如果你想自己传递这些参数也是可以的,后面会介绍一下。
        而gl_Vertex和gl_Normal则分别表示当前传入的顶点的物体坐标系坐标和表面法向量,
        gl_Position则是用来传输投影坐标系内顶点坐标的内建变量。
        注意内建变量是不用声明的,直接使用就行
        /

        vec3 ECPosition = vec3(gl_ModelViewMatrix * gl_Vertex);

        vec3 N = normalize(gl_NormalMatrix * gl_Normal);
        vec3 L = normalize(lightposition - ECPosition);
        vec3 V = normalize(eyeposition - ECPosition);
        vec3 H = normalize(V + L);

        vec3 diffuse = lightcolor max(dot(N , L) , 0);
        vec3 specular = lightcolor
        pow(max(dot(N , H) , 0) , Ns) * attenuation;

        color = vec4(clamp((diffuse + specular) , 0.0 , 1.0) , 1.0);
        color = color + ambient;

        gl_Position = ftransform();
        }

  2. 片断着色器(像素着色器)

    • 在coco2dx中是fsh文件

      片段着色器可以处理的操作是:

      1.提取纹理单元,用于纹理贴图。
      2.纹理应用。
      3.雾.
      4.主颜色和辅助颜色汇合。

  3. Shader数据访问类型,以及app和Shader数据的交互

    GLSL 1.3版本以前

    顶点着色器的输入变量用attribute关键字来限定
    attribute不能作为片段着色器的输入
    片段着色器的输入用varying关键字限定
    顶点着色器修改varying变量的值
    片段着色器使用varying字段的值。
    

    GLSL 1.4版本中

    attribute和varying字段都删除了
    都统一使用in out或inout关键字
    

    Shader全局变量用uniform关键字修饰

    uniform只能在app中修改
    vertex和fragment shader只可以使用
    uniform变量一般用来表示:变换矩阵,材质各种光照颜色,颜色等信息
    
  1. 实例

    • 让图形沿x轴3d翻转

      .vsh文件

      uniform float time;
      void main()
      {

      gl_FrontColor = gl_Color;   
      
      vec4 v = vec4(gl_Vertex);  
      v.y=v.y*cos(time)+v.y*sin(time);  
      v.z=-v.y*sin(time)+cos(time)*v.z;  
      gl_Position = gl_ModelViewProjectionMatrix * v;  
      

      }

      .fsh文件

      void main()
      {

      gl_FragColor = gl_Color;  
      

      }

    • 基于Perlin噪声的火球效果

      http://blog.csdn.net/panda1234lee/article/details/52085375

      cocos2dx 调用代码

      auto fileUtiles = FileUtils::getInstance();
      auto fragmentFullPath = fileUtiles->fullPathForFilename(“filter/01.fsh”);
      auto fragSource = fileUtiles->getStringFromFile(fragmentFullPath);
      auto glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, fragSource.c_str());
      //auto glprogram = GLProgram::createWithByteArrays(fragSource.c_str(), ccPositionTextureColor_noMVP_frag);
      //auto glprogram = GLProgram::createWithFilenames(“filter/01.vsh”, “filter/01.fsh”);

      cocos2d::GLProgramState _glprogramstate = (glprogram == nullptr ? nullptr : GLProgramState::getOrCreateWithGLProgram(glprogram));
      Scale9Sprite
      cover = Scale9Sprite::createWithSpriteFrameName(“button.png”);
      cover->setPreferredSize(Size(REAL_WINSIZE_W, REAL_WINSIZE_H));
      cover->setAnchorPoint(Vec2::ZERO);
      this->addChild(cover);
      cover->setGLProgramState(_glprogramstate);
      cover->getGLProgramState()->setUniformVec2(“iResolution”, Vec2(cover->getTexture()->getContentSizeInPixels().width, cover->getTexture()->getContentSizeInPixels().height));
      BlendFunc cbl = { GL_ONE , GL_ONE };
      cover->setBlendFunc(cbl);

      .fsh文件

      uniform vec2 iResolution;
      float iGlobalTime = CC_Time[1];

      float snoise(vec3 uv, float res)
      {

      // ❤  
      const vec3 s = vec3(1e0, 1e2, 1e3);  
      //const vec3 s = vec3(1., 100., 1000.);  
      
      uv *= res;  
      
      vec3 uv0 = floor(mod(uv, res))*s;  
      vec3 uv1 = floor(mod(uv+vec3(1.), res))*s;  
      
      vec3 f = fract(uv);   
      // 缓和函数  
      f = f*f*(3.0-2.0*f);  
      
      //  ❤扭曲图像   
      vec4 v = vec4(uv0.x+uv0.y+uv0.z, uv1.x+uv0.y+uv0.z,  
                  uv0.x+uv1.y+uv0.z, uv1.x+uv1.y+uv0.z);  
      //vec4 v = vec4(uv0.x, uv0.y, uv1.x, uv1.y);  
      
       // ❤ 影响形状和速度  
       vec4 r = fract(sin(v*1e-1)*1e3);  
       //vec4 r = fract(sin(v));  
       float r0 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y);  

       // ❤ 影响形状和速度  
       r = fract(sin((v + uv1.z - uv0.z)*1e-1)*1e3);  
       //r = fract(sin(v));  
       float r1 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y);  

       return mix(r0, r1, f.z)*2.-1.;  
    }  

    void main(void)   
    {  
       // 换算到[(-.5, -.5), (.5, .5)]  
       vec2 p = -.5 + gl_FragCoord.xy / iResolution.xy;  

       // 换算到[(-1., -1.), (1., 1.)]  
       //vec2 p = (2.*gl_FragCoord.xy - iResolution.xy) / iResolution.xy;  
       //p *= 0.5; // 放大2倍  

       // 根据屏幕纵横比变换  
       p.x *= iResolution.x/iResolution.y;  

       // 屏幕中心到边界,亮度由高到低  
       // 定义火焰的基本形状  
       float color = 3.0 - (3.*length(2.*p));  
       //float color = 3.0 - (3.*length(2.*p - vec2(-.5, .5)));  
       //float color = 3.0 - (3.*length(2.*p - vec2(2*p.x, 0.0)));  

       // ❤ 控制火焰发散的形式  
       vec3 coord = vec3(atan(p.x,p.y)/6.2832+.5, length(p)*.4, 0.5);  
       //vec3 coord = vec3(p.y, 0, 0);  
       //vec3 coord = vec3(p.x, 0, 0);  
       //vec3 coord = vec3(atan(p.x,p.y), 0, 0);  
       //vec3 coord = vec3(length(p)*.4, 0, 0);  

       // 控制颜色的层次  
       for(int i = 1; i <= 7; i++)  
       {  
          float power = pow(2.0, float(i));  
          color += (1.5 / power) *   
          snoise(coord + vec3(0.,-iGlobalTime*.05, iGlobalTime*.01), power*16.);  
          //snoise(coord + vec3(0., 0.05, 0.01), power*16.);  
       }  
       gl_FragColor = vec4( color, pow(max(color,0.),2.)*0.4, pow(max(color,0.),3.)*0.15 , 1.0);  
    }

* 若干常见噪声类型

.fsh文件

    // 算法解析:创建一个由若干虚拟晶格组成的平面,接着给每个晶格的顶点赋予一个随机的向量(通过hash函数生成),
    // 然后通过fract函数将该点平移到【x:0-1, y:0-1】的空间中,再计算到各个晶格顶点的距离向量,
    // 然后将这两个向量进行dot,最后dot的结果利用ease curves(即u)进行双线性插值。

    // 注意:Gradient Noise并不是Value Noise,也不是Perlin Noise,而是基于Perlin Noise的一种分形布朗运动
    //(Fractal Brownian Motion,FBM)的叠加


    uniform vec2 iResolution;  

    vec2 hash22( vec2 p )
    {
        p = vec2( dot(p,vec2(127.1,311.7)),
                  dot(p,vec2(269.5,183.3)) );

        return -1.0 + 2.0*fract(sin(p)*43758.5453123);
    }

    float hash21(vec2 p)
    {
        return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
        //vec3 p3  = fract(vec3(p.xyx) * .1931);
        //p3 += dot(p3, p3.yzx + 19.19);
        //return fract((p3.x + p3.y) * p3.z);
    }

    // =================================================================================

    float noise( in vec2 p )
    {
        vec2 i = floor( p );
        vec2 f = fract( p );

        // Ease Curve
        //vec2 u = f*f*(3.0-2.0*f);
        vec2 u = f*f*f*(6.0*f*f - 15.0*f + 10.0);

        return mix( mix( dot( hash22( i + vec2(0.0,0.0) ), f - vec2(0.0,0.0) ), 
                         dot( hash22( i + vec2(1.0,0.0) ), f - vec2(1.0,0.0) ), u.x),
                    mix( dot( hash22( i + vec2(0.0,1.0) ), f - vec2(0.0,1.0) ), 
                       dot( hash22( i + vec2(1.0,1.0) ), f - vec2(1.0,1.0) ), u.x), u.y);

        //return dot(hash22(i+vec2(0.0, 0.0)), f-vec2(0.0, 0.0));
        //return dot(hash22(i+vec2(1.0, 0.0)), f-vec2(1.0, 0.0));
        //return mix(dot(hash22(i+vec2(0.0, 0.0)), f-vec2(0.0, 0.0)),
        //           dot(hash22(i+vec2(1.0, 0.0)), f-vec2(1.0, 0.0)), u.x);

        //return dot(hash22(i+vec2(0.0, 1.0)), f-vec2(0.0, 1.0));
        //return dot(hash22(i+vec2(1.0, 1.0)), f-vec2(1.0, 1.0));
        //return mix(dot(hash22(i+vec2(0.0, 1.0)), f-vec2(0.0, 1.0)),
        //           dot(hash22(i+vec2(1.0, 1.0)), f-vec2(1.0, 1.0)), u.x);
    }

    float noise_fractal(in vec2 p)
    {
        p *= 5.0;
        mat2 m = mat2( 1.6,  1.2, -1.2,  1.6 );
        float f  = 0.5000*noise(p); p = m*p;
        f += 0.2500*noise(p); p = m*p;
        f += 0.1250*noise(p); p = m*p;
        f += 0.0625*noise(p); p = m*p;

        return f;
    }


    float noise_sum_abs(vec2 p)
    {
        float f = 0.0;
        p = p * 7.0;
        f += 1.0000 * abs(noise(p)); p = 2.0 * p;
        f += 0.5000 * abs(noise(p)); p = 2.0 * p;
        f += 0.2500 * abs(noise(p)); p = 2.0 * p;
        f += 0.1250 * abs(noise(p)); p = 2.0 * p;
        f += 0.0625 * abs(noise(p)); p = 2.0 * p;

        return f;
    }

    float value_noise(vec2 p)
    {
        p *= 56.0;
        vec2 pi = floor(p);
        //vec2 pf = p - pi;
        vec2 pf = fract(p);

        vec2 w = pf * pf * (3.0 - 2.0 * pf);

        // 它把原来的梯度替换成了一个简单的伪随机值,我们也不需要进行点乘操作,
        // 而直接把晶格顶点处的随机值按权重相加即可。
        return mix(mix(hash21(pi + vec2(0.0, 0.0)), hash21(pi + vec2(1.0, 0.0)), w.x),
                  mix(hash21(pi + vec2(0.0, 1.0)), hash21(pi + vec2(1.0, 1.0)), w.x),
                  w.y);
    }

    float simplex_noise(vec2 p)
    {
        const float K1 = 0.366025404; // (sqrt(3)-1)/2;
        const float K2 = 0.211324865; // (3-sqrt(3))/6;
        // 变换到新网格的(0, 0)点
        vec2 i = floor(p + (p.x + p.y) * K1);
        // i - (i.x+i.y)*K2换算到旧网格点
        // a:变形前输入点p到该网格点的距离
        vec2 a = p - (i - (i.x + i.y) * K2);
        vec2 o = (a.x < a.y) ? vec2(0.0, 1.0) : vec2(1.0, 0.0);
        // 新网格(1.0, 0.0)或(0.0, 1.0)
        // b = p - (i+o - (i.x + i.y + 1.0)*K2);
        vec2 b = a - o + K2;
        // 新网格(1.0, 1.0)
        // c = p - (i+vec2(1.0, 1.0) - (i.x+1.0 + i.y+1.0)*K2);
        vec2 c = a - 1.0 + 2.0 * K2;
        // 计算每个顶点的权重向量,r^2 = 0.5
        vec3 h = max(0.5 - vec3(dot(a, a), dot(b, b), dot(c, c)), 0.0);
        // 每个顶点的梯度向量和距离向量的点乘,然后再乘上权重向量
        vec3 n = h * h * h * h * vec3(dot(a, hash22(i)), dot(b, hash22(i + o)), dot(c, hash22(i + 1.0)));

        // 之所以乘上70,是在计算了n每个分量的和的最大值以后得出的,这样才能保证将n各个分量相加以后的结果在[-1, 1]之间
        return dot(vec3(70.0, 70.0, 70.0), n);
    }

    // -----------------------------------------------

    void main()
    {
        vec2 p = gl_FragCoord.xy / iResolution.xy;

        vec2 uv = p * vec2(iResolution.x/iResolution.y,1.0);

        float f = 0.0;

        // 1: perlin noise    
        if( p.x<0.2 )
        {
            f = noise( 16.0 * uv );
        }
        // 2: fractal noise (4 octaves)
        else if(p.x>=0.2 && p.x<0.4)    
        {
            f = noise_fractal(uv);
        }
        // 3:fractal abs noise
        else if(p.x>=0.4 && p.x<0.6)
        {
            f = noise_sum_abs(uv);
        }
        // 4: value noise
        else if(p.x>=0.6 && p.x<0.8)
        {
            f = value_noise(uv);
        }
        // 5:simplex_noise
        else
        {
            f = simplex_noise(16.0*uv);
        }

        f = 0.5 + 0.5*f;

        // 分割线:注意如果第三个参数超过了限定范围就不进行插值
        f *= smoothstep(0.0, 0.005, abs(p.x-0.2));
        f *= smoothstep(0.0, 0.005, abs(p.x-0.4));    
        f *= smoothstep(0.0, 0.005, abs(p.x-0.6));
        f *= smoothstep(0.0, 0.005, abs(p.x-0.8));

        gl_FragColor = vec4( f, f, f, 1.0 );
    }

Ray-Marching(光线步进) 多个立体图形的绘制

http://blog.csdn.net/panda1234lee/article/details/57085659

    // Created by inigo quilez - iq/2013
    // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

    // A list of usefull distance function to simple primitives, and an example on how to
    // do some interesting boolean operations, repetition and displacement.
    //
    // More info here: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm

    // https://www.shadertoy.com/view/Xds3zN
    // 抗锯齿开关
    #define AA 2   // make this 1 is your machine is too slow
    //------------------------------------------------------------------


    // --------SDF--------
    // http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm

    float sdPlane(vec3 p)
    {
        return p.y;
    }

    float sdSphere(vec3 p, float s)
    {
        return length(p) - s;
    }

    float sdBox(vec3 p, vec3 b)
    {
        vec3 d = abs(p) - b;
        return min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0));
    }

    float sdEllipsoid( in vec3 p, in vec3 r )
    {
        return (length( p/r ) - 1.0) * min(min(r.x,r.y),r.z);
    }

    float udRoundBox(vec3 p, vec3 b, float r)
    {
        return length(max(abs(p) - b, 0.0)) - r;
    }

    float sdTorus(vec3 p, vec2 t)
    {
        return length(vec2(length(p.xz) - t.x, p.y)) - t.y;
    }

    float sdHexPrism(vec3 p, vec2 h)
    {
        vec3 q = abs(p);
    #if 0
        return max(q.z-h.y,max((q.x*0.866025+q.y*0.5),q.y)-h.x);
    #else
        float d1 = q.z - h.y;
        float d2 = max((q.x * 0.866025 + q.y * 0.5), q.y) - h.x;
        return length(max(vec2(d1, d2), 0.0)) + min(max(d1, d2), 0.);
    #endif
    }

    float sdCapsule(vec3 p, vec3 a, vec3 b, float r)
    {
        vec3 pa = p - a, ba = b - a;
        float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
        return length(pa - ba * h) - r;
    }

    float sdTriPrism(vec3 p, vec2 h)
    {
        vec3 q = abs(p);
    #if 0
        return max(q.z-h.y,max(q.x*0.866025+p.y*0.5,-p.y)-h.x*0.5);
    #else
        float d1 = q.z - h.y;
        float d2 = max(q.x * 0.866025 + p.y * 0.5, -p.y) - h.x * 0.5;
        return length(max(vec2(d1, d2), 0.0)) + min(max(d1, d2), 0.);
    #endif
    }

    float sdCylinder(vec3 p, vec2 h)
    {
        vec2 d = abs(vec2(length(p.xz), p.y)) - h;
        return min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
    }

    float sdCone( in vec3 p, in vec3 c )
    {
        vec2 q = vec2( length(p.xz), p.y );
        float d1 = -q.y-c.z;
        float d2 = max( dot(q,c.xy), q.y);
        return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.);
    }

    float sdConeSection( in vec3 p, in float h, in float r1, in float r2 )
    {
        float d1 = -p.y - h;
        float q = p.y - h;
        float si = 0.5*(r1-r2)/h;
        float d2 = max( sqrt( dot(p.xz,p.xz)*(1.0-si*si)) + q*si - r2, q );
        return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.);
    }

    float sdPryamid4(vec3 p, vec3 h) // h = { cos a, sin a, height }
    {
        // Tetrahedron = Octahedron - Cube
        float box = sdBox(p - vec3(0, -2.0 * h.z, 0), vec3(2.0 * h.z));

        float d = 0.0;
        d = max(d, abs(dot(p, vec3(-h.x, h.y, 0))));
        d = max(d, abs(dot(p, vec3(h.x, h.y, 0))));
        d = max(d, abs(dot(p, vec3(0, h.y, h.x))));
        d = max(d, abs(dot(p, vec3(0, h.y, -h.x))));
        float octa = d - h.z;
        return max(-box, octa); // Subtraction
    }
    // --------SDF--------

    // (p.x^2 + p.y^2)^(1/2)
    float length2(vec2 p)
    {
        return sqrt(p.x * p.x + p.y * p.y);
    }

    // (p.x^6 + p.y^6)^(1/6)
    float length6(vec2 p)
    {
        p = p * p * p;
        p = p * p;
        return pow(p.x + p.y, 1.0 / 6.0);
    }

    // (p.x^8 + p.y^8)^(1/8)
    float length8(vec2 p)
    {
        p = p * p;
        p = p * p;
        p = p * p;
        return pow(p.x + p.y, 1.0 / 8.0);
    }

    float sdTorus82(vec3 p, vec2 t)
    {
        vec2 q = vec2(length2(p.xz) - t.x, p.y);
        return length8(q) - t.y;
    }

    float sdTorus88(vec3 p, vec2 t)
    {
        vec2 q = vec2(length8(p.xz) - t.x, p.y);
        return length8(q) - t.y;
    }

    float sdCylinder6(vec3 p, vec2 h)
    {
        return max(length6(p.xz) - h.x, abs(p.y) - h.y);
    }

    //------------------------------------------------------------------
    // Subtraction
    float opS(float d1, float d2)
    {
        return max(-d2, d1);
    }

    // Union
    vec2 opU(vec2 d1, vec2 d2)
    {
        return (d1.x < d2.x) ? d1 : d2;
    }

    // Repetition
    vec3 opRep(vec3 p, vec3 c)
    {
        return mod(p, c) - 0.5 * c;
    }

    // Twist
    vec3 opTwist(vec3 p)
    {
        float c = cos(10.0 * p.y + 10.0);
        float s = sin(10.0 * p.y + 10.0);
        mat2 m = mat2(c, -s, s, c);
        return vec3(m * p.xz, p.y);
    }

    //------------------------------------------------------------------

    vec2 map( in vec3 pos )
    {
        // res.x 是光线到表面的距离,res.y 影响的是物体材质
        vec2 res = opU( vec2( sdPlane( pos), 1.0 ),
                vec2( sdSphere( pos-vec3( 0.0,0.25, 0.0), 0.25 ), 46.9 ) );
        res = opU( res, vec2( sdBox( pos-vec3( 1.0,0.25, 0.0), vec3(0.25) ), 3.0 ) );
        res = opU( res, vec2( udRoundBox( pos-vec3( 1.0,0.25, 1.0), vec3(0.15), 0.1 ), 41.0 ) );
        res = opU( res, vec2( sdTorus( pos-vec3( 0.0,0.25, 1.0), vec2(0.20,0.05) ), 25.0 ) );
        res = opU( res, vec2( sdCapsule( pos,vec3(-1.3,0.10,-0.1), vec3(-0.8,0.50,0.2), 0.1 ), 31.9 ) );
        res = opU( res, vec2( sdTriPrism( pos-vec3(-1.0,0.25,-1.0), vec2(0.25,0.05) ),43.5 ) );
        res = opU( res, vec2( sdCylinder( pos-vec3( 1.0,0.30,-1.0), vec2(0.1,0.2) ), 8.0 ) );
        res = opU( res, vec2( sdCone( pos-vec3( 0.0,0.50,-1.0), vec3(0.8,0.6,0.3) ), 55.0 ) );
        res = opU( res, vec2( sdTorus82( pos-vec3( 0.0,0.25, 2.0), vec2(0.20,0.05) ),50.0 ) );
        res = opU( res, vec2( sdTorus88( pos-vec3(-1.0,0.25, 2.0), vec2(0.20,0.05) ),43.0 ) );
        res = opU( res, vec2( sdCylinder6( pos-vec3( 1.0,0.30, 2.0), vec2(0.1,0.2) ), 12.0 ) );
        res = opU( res, vec2( sdHexPrism( pos-vec3(-1.0,0.20, 1.0), vec2(0.25,0.05) ),17.0 ) );
        res = opU( res, vec2( sdPryamid4( pos-vec3(-1.0,0.15,-2.0), vec3(0.8,0.6,0.25) ),37.0 ) );
        res = opU( res, vec2( opS( udRoundBox( pos-vec3(-2.0,0.2, 1.0), vec3(0.15),0.05),
                                sdSphere( pos-vec3(-2.0,0.2, 1.0), 0.25)), 13.0 ) );
        res = opU( res, vec2( opS( sdTorus82( pos-vec3(-2.0,0.2, 0.0), vec2(0.20,0.1)),
                                sdCylinder( opRep( vec3(atan(pos.x+2.0,pos.z)/6.2831, pos.y, 0.02+0.5*length(pos-vec3(-2.0,0.2, 0.0))), vec3(0.05,1.0,0.05)), vec2(0.02,0.6))), 51.0 ) );
        res = opU( res, vec2( 0.5*sdSphere( pos-vec3(-2.0,0.25,-1.0), 0.2 ) + 0.03*sin(50.0*pos.x)*sin(50.0*pos.y)*sin(50.0*pos.z), 65.0 ) );
        res = opU( res, vec2( 0.5*sdTorus( opTwist(pos-vec3(-2.0,0.25, 2.0)),vec2(0.20,0.05)), 46.7 ) );
        res = opU( res, vec2( sdConeSection( pos-vec3( 0.0,0.35,-2.0), 0.15, 0.2, 0.1 ), 13.67 ) );
        res = opU( res, vec2( sdEllipsoid( pos-vec3( 1.0,0.35,-2.0), vec3(0.15, 0.2, 0.05) ), 43.17 ) );

        return res;
    }

    // 实际是 Ray-marching
    vec2 castRay( in vec3 ro, in vec3 rd )
    {
        float tmin = 1.0;
        float tmax = 20.0;

    #if 1   // 加速 Raymarching
        // bounding volume
        float tp1 = (0.0-ro.y)/rd.y;
        if( tp1>0.0 )
            tmax = min( tmax, tp1 );

        float tp2 = (1.6-ro.y)/rd.y;
        if( tp2>0.0 )
        {
            if( ro.y>1.6 )
                tmin = max( tmin, tp2 );
            else
                tmax = min( tmax, tp2 );
        }
    #endif

        float t = tmin;
        float m = -1.0;
        for( int i=0; i<64; i++ )
        {
            float precis = 0.0005*t;
            vec2 res = map( ro+rd*t );
            if( res.x<precis || t>tmax )
                break;
            t += res.x;
            m = res.y;    // 唯一用到 res.y 的地方,影响材质的计算
        }

        if( t>tmax )
            m=-1.0;
        return vec2( t, m );
    }

    // 柔化阴影
    // http://www.iquilezles.org/www/articles/rmshadows/rmshadows.htm
    float softshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax )
    {
        float res = 1.0;
        float t = mint;
        for( int i=0; i<16; i++ )
        {
            float h = map( ro + rd*t ).x;
            res = min( res, 8.0*h/t );
            t += clamp( h, 0.02, 0.10 );
            if( h<0.001 || t>tmax )    // 在[ mint, maxt)范围内进行插值
                break;
        }
        return clamp( res, 0.0, 1.0 );
    }

    // 法线
    vec3 calcNormal( in vec3 pos )
    {
        vec2 e = vec2(1.0,-1.0)*0.5773*0.0005;
        return normalize( e.xyy*map( pos + e.xyy ).x +
                e.yyx*map( pos + e.yyx ).x +
                e.yxy*map( pos + e.yxy ).x +
                e.xxx*map( pos + e.xxx ).x );
        /*
         vec3 eps = vec3( 0.0005, 0.0, 0.0 );
         vec3 nor = vec3(
         map(pos+eps.xyy).x - map(pos-eps.xyy).x,
         map(pos+eps.yxy).x - map(pos-eps.yxy).x,
         map(pos+eps.yyx).x - map(pos-eps.yyx).x );
         return normalize(nor);
         */
    }

    // Ambient Occlusion: 环境光吸收/遮蔽
    float calcAO( in vec3 pos, in vec3 nor )
    {
        float occ = 0.0;
        float sca = 1.0;
        for( int i=0; i<5; i++ )
        {
            float hr = 0.01 + 0.12*float(i)/4.0;
            vec3 aopos = nor * hr + pos;
            float dd = map( aopos ).x;
            occ += -(dd-hr)*sca;
            sca *= 0.95;
        }
        return clamp( 1.0 - 3.0*occ, 0.0, 1.0 );
    }

    vec3 render( in vec3 ro, in vec3 rd )
    {
        vec3 col = vec3(0.7, 0.9, 1.0) +rd.y*0.8;
        vec2 res = castRay(ro,rd);
        float t = res.x;
        float m = res.y;
        if( m>-0.5 )
        {
            vec3 pos = ro + t*rd;        // 步进的光线位置
            vec3 nor = calcNormal( pos );    // 法线
            vec3 ref = reflect( rd, nor );    // 反光

            // material
            col = 0.45 + 0.35*sin( vec3(0.05,0.08,0.10)*(m-1.0) );
            // 如果是地板的话
            if( m<1.5 )
            {
                // 格子地砖
                float f = mod( floor(5.0*pos.z) + floor(5.0*pos.x), 2.0);
                col = 0.3 + 0.1*f*vec3(1.0);
            }

            // 光照模型的计算
            float occ = calcAO( pos, nor );
            vec3 lig = normalize( vec3(-0.4, 0.7, -0.6) );
            float amb = clamp( 0.5+0.5*nor.y, 0.0, 1.0 );
            float dif = clamp( dot( nor, lig ), 0.0, 1.0 );
            float bac = clamp( dot( nor, normalize(vec3(-lig.x,0.0,-lig.z))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0);
            float dom = smoothstep( -0.1, 0.1, ref.y );
            float fre = pow( clamp(1.0+dot(nor,rd),0.0,1.0), 2.0 );
            float spe = pow(clamp( dot( ref, lig ), 0.0, 1.0 ),16.0);

            // 散射阴影
            dif *= softshadow( pos, lig, 0.02, 2.5 );
            // 反光阴影
            dom *= softshadow( pos, ref, 0.02, 2.5 );

            // 注意物体底座的阴影变化
            // occ = 1.;
            vec3 lin = vec3(0.0);
            lin += 1.30*dif*vec3(1.00,0.80,0.55);
            lin += 2.00*spe*vec3(1.00,0.90,0.70)*dif;
            lin += 0.40*amb*vec3(0.40,0.60,1.00)*occ;
            lin += 0.50*dom*vec3(0.40,0.60,1.00)*occ;
            lin += 0.50*bac*vec3(0.25,0.25,0.25)*occ;
            lin += 0.25*fre*vec3(1.00,1.00,1.00)*occ;
            col = col*lin;

            col = mix( col, vec3(0.8,0.9,1.0), 1.0-exp( -0.0002*t*t*t ) );
        }

        return vec3( clamp(col,0.0,1.0) );
    }

    mat3 setCamera( in vec3 ro, in vec3 ta, float cr )
    {
        vec3 cw = normalize(ta-ro);        // look
        vec3 cp = vec3(sin(cr), cos(cr),0.0);    // XY Space
        vec3 cu = normalize( cross(cw,cp) );    // right
        vec3 cv = normalize( cross(cu,cw) );    // up

        // right,
        // up,    * world = camera
        // look
        // 注意: glsl 是按列存储,所以可以直接右乘相机坐标系的点,结果就是世界坐标系下的点
        return mat3( cu, cv, cw );    // right, up, look
    }

    uniform  vec2 iResolution;
    float iGlobalTime = CC_Time[1];
    vec2 iMouse = vec2(500, 500);

    #define fragColor gl_FragColor
    #define fragCoord  gl_FragCoord

    void main()
    {
        vec2 mo = iMouse.xy / iResolution.xy;
        float time = 15.0 + iGlobalTime;

        vec3 tot = vec3(0.0);
    #if AA>1    // 开启 AA
        for (int m = 0; m < AA; m++)
        {
            for (int n = 0; n < AA; n++)
            {
                // pixel coordinates
                vec2 o = vec2(float(m), float(n)) / float(AA) - 0.5;
                vec2 p = (-iResolution.xy + 2.0 * (fragCoord.xy + o)) / iResolution.y;
    #else
                vec2 p = (-iResolution.xy + 2.0*fragCoord.xy)/iResolution.y;
    #endif

                // camera
                vec3 ro = vec3(-0.5 + 3.5 * cos(0.1 * time + 6.0 * mo.x),
                        1.0 + 2.0 * mo.y, 0.5 + 4.0 * sin(0.1 * time + 6.0 * mo.x));
                vec3 ta = vec3(-0.5, -0.4, 0.5);
                // camera-to-world transformation
                mat3 ca = setCamera(ro, ta, 0.0);
                // ray direction
                vec3 rd = ca * normalize(vec3(p.xy, 2.0));

                // render
                vec3 col = render(ro, rd);

                // 针对电子屏幕的 gamma 矫正(否则颜色偏暗)
                col = pow(col, vec3(0.4545));

                tot += col;
    #if AA>1
            }
        }
        tot /= float(AA * AA);
    #endif

        fragColor = vec4(tot, 1.0);
    }

一个动态的火苗

    #ifdef GL_ES
    precision mediump float;
    #endif

    float time = CC_Time[1];
    const vec2 resolution = vec2(640.0, 640.0);
    void main( void ) 
    {
       vec2 pos = -1. + 2.*gl_FragCoord.xy / resolution.xy;
       pos *= vec2(resolution.x / resolution.y, 1.) * 3.;

       // 火苗的抖动
       if(pos.y>-2.*4.2)
       {
          for(float baud = 1.; baud < 9.; baud += 1.)
          {
             pos.y += 0.2*sin(4.20*time/(1.+baud))/(1.+baud);
             pos.x += 0.1*cos(pos.y/4.20+2.40*time/(1.+baud))/(1.+baud);
          }
          pos.y += 0.04*fract(sin(time*60.));
       }

       // 火苗外焰
       vec3 color = vec3(0.,0.,0.);
       float p =.004;
       float y = -pow(abs(pos.x), 4.2)/p;   // 外焰的形状,注意pos.x负数会被截断
       float dir = abs(pos.y - y)*sin(.3);  // 外焰的大小(扩大渐变区域)
       //float dir = abs(pos.y - y)*(0.01*sin(time)+0.07);
       if(dir < 0.7)
       {
          color.rg += smoothstep(0.,1.,.75-dir);   // 外焰颜色渐变
          color.g /=2.4;                           // 减点绿
       }
       color *= (0.2 + abs(pos.y/4.2 + 4.2)/4.2);  // 增加对比度
       color += pow(color.r, 1.1);                 // 加点红
       color *= cos(-0.5+pos.y*0.4);               // 隐藏底部的颜色

       // 火苗内焰
       pos.y += 1.5;
       vec3 dolor = vec3(0.,0.,0.0);
       y = -pow(abs(pos.x), 4.2)/(4.2*p)*4.2;   // 内焰的形状,注意和外焰的次幂,越接近越不容易穿帮
       dir = abs(pos.y - y)*sin(1.1);           // 内焰的大小(扩大渐变区域)
       if(dir < 0.7)
       {
          dolor.bg += smoothstep(0., 1., .75-dir);// 内焰颜色渐变
          dolor.g /=2.4;
       }
       dolor *= (0.2 + abs((pos.y/4.2+4.2))/4.2);
       dolor += pow(color.b,1.1);                 // 加点蓝
       dolor *= cos(-0.6+pos.y*0.4);
       //dolor.rgb -= pow(length(dolor)/16., 0.5);

       color = (color+dolor)/2.;
       gl_FragColor = vec4(vec3(color) , 1.0 );

    }

“Barrel Blur”的实现

    #ifdef GL_ES
    precision highp float;
    #endif

    uniform sampler2D texture;

    const vec2 sketchSize = vec2(512., 512.);
    const float barrelPower = 0.4; 
    const int num_iter = 10;
    const float reci_num_iter_f = 1.0 / float(num_iter); // 用于迭代时归一化

    vec2 barrelDistortion(vec2 coord, float amt) // 随着迭代次数增加,像素偏移更大;距离图像中心越远,像素偏移更大。
    {
        vec2 cc = coord - 0.5;
        float dist = dot(cc, cc);
        return coord + cc * dist * amt; 
    }

    float sat( float t )
    {
        return clamp( t, 0.0, 1.0 );
    }

    float linterp( float t ) {
        return sat( 1.0 - abs( 2.0*t - 1.0 ) );
    }

    float remap( float t, float a, float b ) 
    {
        return sat( (t - a) / (b - a) );
    }

    vec3 spectrum_offset( float t ) 
    {
        vec3 ret;
        float lo = step(t,0.5);
        float hi = 1.0-lo;
        float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );
        ret = vec3(lo,1.0,hi) * vec3(1.0-w, w, 1.0-w);

        return pow( ret, vec3(1.0/2.2) );
    }

    void main()
    {   
        vec2 uv=(gl_FragCoord.xy/sketchSize.xy);

        vec3 sumcol = vec3(0.0);
        vec3 sumw = vec3(0.0);  
        for ( int i=0; i<num_iter;++i )
        {
            float t = float(i) * reci_num_iter_f;
            vec3 w = spectrum_offset( t );   // RGB三通道分别的权重
            sumw += w;                       // 用于之后归一化权重
            sumcol += w * texture2D( texture, barrelDistortion(uv, barrelPower*t ) ).rgb; 
        } 

        gl_FragColor = vec4(sumcol.rgb / sumw, 1.0);
    }

* Brush Smear

.fsh

     #define fragColor gl_FragColor
     #define fragCoord  gl_FragCoord

    vec3 saturate(vec3 a){return clamp(a,0.,1.);}
    float opS( float d2, float d1 ){return max(-d1,d2);}
    float rand(vec2 co){
        return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
    }
    float rand(float n){
         return fract(cos(n*89.42)*343.42);
    }

    float dtoa(float d, float amount)
    {
        return clamp(1.0 / (clamp(d, 1.0/amount, 1.0)*amount), 0.,1.);
    }
    float sdColumn(vec2 uv, float xmin, float xmax)
    {
        return max(xmin-uv.x, uv.x-xmax);
    }
    float sdAxisAlignedRect(vec2 uv, vec2 tl, vec2 br)
    {
        vec2 d = max(tl-uv, uv-br);
        return length(max(vec2(0.0), d)) + min(0.0, max(d.x, d.y));
    }

    // 0-1 1-0
    float smoothstep4(float e1, float e2, float e3, float e4, float val)
    {
        return min(smoothstep(e1,e2,val), 1.-smoothstep(e3,e4,val));
    }

    const float left = 1.82;
    const float right = 2.08;

    vec3 texturize(vec2 uv, vec3 inpColor, float dist)
    {
        float falloffY = 1.0 - smoothstep4(-0.5, 0.1, 0.4, 1., uv.y);
        float falloffX = (smoothstep(left, right, uv.x)) * 0.6;
        dist -= falloffX * pow(falloffY, 0.6) * 0.09;


        float amt = 13. + (max(falloffX, falloffY) * 600.);

        return mix(inpColor, vec3(0.), dtoa(dist, amt));
    }

    float map(vec2 uv)
    {
        uv.x += rand(uv.y) * 0.006;// some distortion in x axis
        return sdColumn(uv, left, right);
    }

    vec2 iResolution = vec2(100,100);

    void main( )
    {
        vec2 uv = fragCoord.xy;
        uv = (uv / iResolution.y * 2.0) - 1.;
        uv.x += cos(uv.y* (uv.x+1.) * 3.) * 0.003;
        uv.y += cos(uv.x * 6.) * 0.00007;

        vec3 col = vec3(1.,1.,0.86);// bg

        // black stroke
        float dist = map(uv);
        col = texturize(uv, col, dist);// ok this is a stupid way to organize this effect. WIP.

        // red-orangeish square.
        dist = sdAxisAlignedRect(uv, vec2(-0.68), vec2(-0.55));
        float amt = 90. + (rand(uv.y) * 100.) + (rand(uv.x / 4.) * 90.);
        float vary = sin(uv.x*uv.y*50.)*0.0047;
        dist = opS(dist-0.028+vary, dist-0.019-vary);// round edges, and hollow it out
        col = mix(col, vec3(0.99,.4, 0.0), dtoa(dist, amt) * 0.7);
        col = mix(col, vec3(0.85,0.,0.), dtoa(dist, 700.));

        uv -= 1.0;// vignette
        float vignetteAmt = 1.-dot(uv*0.5,uv* 0.12);
        col *= vignetteAmt;

        // grain
        col.rgb += (rand(uv)-.5)*.07;
        col.rgb = saturate(col.rgb);


        fragColor = vec4(col, 1.);
    }