OpenGL

基础

GLSL

0. 调试工具ShaderDesigner

* GLSL 内建函数

  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语言作为基础高阶着色语言

  2. 顶点着色器(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();
      

      }

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

    • 在coco2dx中是fsh文件

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

     1.提取纹理单元,用于纹理贴图。
     2.纹理应用。
     3.雾.
     4.主颜色和辅助颜色汇合。
    
  4. 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变量一般用来表示:变换矩阵,材质各种光照颜色,颜色等信息
    
  5. 实例

    • 让图形沿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.);
     }