Shader基础技巧整理
自从接触了shader之后我便深深得爱上了它,因为它独特的编程思考方式冲击着我这十几年的惯性认知。 在向各位大佬学习的过程中,每学到一个新的技巧,我都不禁感叹:“实在是妙!” 本文将整理一些个人常用的shader技巧/方法,只包含片元着色器相关内容。 由于本人尚属初学,所以内容会比较基础。
简单几何图形
区间(带通)
两个阶梯函数叠加构成的带通函数,用数字信号处理的角度去思考貌似是个不错的选择
float Band(float v, float start, float end) {
float up = step(start, v);
float down = 1.0 - step(end, v);
return up * down;
}
矩形
x, y两个方向的带通函数叠加
float Rect(vec2 uv, float l, float b, float r, float t) {
float x = Band(uv.x, l, r);
float y = Band(uv.y, b, t);
return x * y;
}
圆形
圆形比较容易出现锯齿,所以用 smoothstep
做平滑处理。
由于 distance()
依赖 sqrt()
开根号,在一些老硬件上会比较耗时,实际使用时可以考虑转换为和r平方进行比较的公式。
float Circle(vec2 uv, vec2 o, float r, float blur) {
return smoothstep(r, r-blur, distance(uv, o));
}
混合叠加
上述几何图形函数的输出值是0或1的float(经过 smoothstep()
可能会出现中间值,不过此处可以不考虑)。
0或1的float值可以利用加、减、乘来模拟位运算。比如上述区间、矩形几何图形都是通过乘法叠加。
加减法例子:ET脸
画一个大圆当脸,减去两个小圆当眼睛
float ETFace(vec2 uv, vec2 o) {
float c = Circle(uv, vec2(.0, .0), 0.5, 0.01);
c -= Circle(uv, vec2(-.2, -.2), 0.2, 0.01);
c -= Circle(uv, vec2(.2, -.2), 0.2, 0.01);
return c;
}
坐标空间处理
Cocos Creator以 左上角
为坐标原点,范围(0, 1)。
shadertoy, GlslEditor中均以 左下角
为坐标原点,范围(0, 1),接下来所有代码将使用这个坐标系。
在绘制某些对称图形时可能需要将原点调整到屏幕中心,即将(0, 1)区间映射到(-0.5, 0.5)。
根据不同场景也可以将(0, 1)区间映射到(-1, 1),哪个处理起来方便用哪个。
// (0, 1)区间映射到(-1, 1)
uv = uv * 2.0 - 1.0;
也可以用下面的方法从任意区间映射到任意区间
float Remap01(float a, float b, float t) {
return (t-a) / (b-a);
}
float Remap(float a, float b, float c, float d, float t) {
return Remap01(a, b, t) * (d-c) + c;
}
长宽适配
不知道这个功能的简称是什么,暂且这么称呼吧。 其作用是在分辨率长宽不等的情况下将坐标空间映射为等边,映射后原先较长的一边其自变量区间会被放大。
void main() {
vec2 uv = gl_FragCoord.xy/u_resolution.xy;
uv = uv * 2.0 - 1.0; // 位移到以中间为原点
uv.x *= u_resolution.x/u_resolution.y; // 将x的自变量区间拉长
float mask = Rect(uv, -0.5, -0.5, 0.5, 0.5);
vec3 color = vec3(mask);
gl_FragColor = vec4(color,1.0);
}
计算角度
用atan()
计算角度,图中将(-PI, PI)区间映射到(0, 1)区间,并且将值对应的灰度输出。
atan()
计算比较耗时,实际项目中慎用。
#define PI 3.141592653589793
void main() {
vec2 uv = gl_FragCoord.xy/u_resolution.xy;
uv = uv * 2.0 - 1.0;
uv.x *= u_resolution.x/u_resolution.y;
float angle = atan(uv.y, uv.x);
angle = Remap(-PI, PI, 0., 1.0, angle);
vec3 color = vec3(angle);
gl_FragColor = vec4(color,1.0);
}
旋转
uv乘以旋转矩阵
#define PI 3.141592653589793
mat2 Rotate2d(float angle){
return mat2(cos(angle), -sin(angle),
sin(angle), cos(angle));
}
void main() {
vec2 uv = gl_FragCoord.xy/u_resolution.xy;
uv = uv * 2.0 - 1.0;
uv.x *= u_resolution.x/u_resolution.y;
uv = Rotate2d(PI / 6.) * uv;
float mask = Rect(uv, -0.5, -0.5, 0.5, 0.5);
vec3 color = vec3(mask);
gl_FragColor = vec4(color,1.0);
}
网格化
将屏幕分割成5x5个网格,每个格子里画一个圆。 原理是将uv拉伸5倍后取小数部分,这样处理后uv会变成每个网格内的局部坐标,这个技巧被广泛使用。
void main() {
// ...
uv = fract(uv * 5.);
float mask = Circle(uv, vec2(0.5), 0.5, 0.01);
// ...
}
噪音(随机化hash)
获取噪音的方法很多很灵活,输入一般是和uv相关的变量,输出(0, 1)范围的1维或2维值。 只要让人肉眼难分辨出模式,就是一个好用的噪音函数。 噪音的用途非常广泛,可以利用噪音降低图片的人工痕迹,后面会单独整理一篇文章。
float Noise1(vec2 p) {
return fract(sin(
dot(p, vec2(12.9898,78.233))
) * 43758.5453123);
}
float Noise2(vec2 p) {
p = frac(p * vec2(123.34, 345.45));
p += dot(p, p + 34.345);
return frac(p.x * p.y);
}
- [Silverlight动画]转向行为 - 机车
- 微信、小游戏与未来
- [Silverlight动画]转向行为 - 2D向量
- [Silverlight动画]转向行为 - 介绍
- WCF技术剖析之十七:消息(Message)详解(中篇)
- flash游戏引擎整理
- [Silverlight动画]转向行为 - 群落
- [Silverlight动画]转向行为 - 路径跟随
- [Silverlight动画]转向行为 - 漫游行为
- [Silverlight动画]转向行为 - 对象回避
- [mobile开发碎碎念]手机页面上显示PDF文件
- Windows Server 2008 R2 配置AD(Active Directory)域控制器
- ios开发基础知识 - 2
- ios开发基础知识 - 1
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法