基础
GLSL
0. 调试工具ShaderDesigner
* GLSL 内建函数
OpenGL着色语言(OpenGL Shading Language)
教程: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语言作为基础高阶着色语言
顶点着色器(Vertex Shader)主要的工作
在coco2dx中是vsh文件
- 利用视图和投影矩阵对点的位置进行变化
- 如果需要利用法线的时候,也同样需要利用视图矩阵对其进行转换
- 纹理坐标的产生和转换
- 顶点的光照或者象素光照的计算
- 颜色计算
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();}
片断着色器(像素着色器)
- 在coco2dx中是fsh文件
片段着色器可以处理的操作是:
1.提取纹理单元,用于纹理贴图。 2.纹理应用。 3.雾. 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变量一般用来表示:变换矩阵,材质各种光照颜色,颜色等信息实例
- 让图形沿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.); }