基础
GLSL
调试工具ShaderDesigner
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
是用来在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.);
}