1-0 参数说明&性能常识
uniform vec3 iResolution; // 窗口分辨率,单位像素
uniform float iTime; // 程序运行的时间,单位秒
uniform float iTimeDelta; // 渲染时间,单位秒
uniform int iFrame; // 帧率
uniform float iChannelTime[4]; // 信道播放时间(秒)
uniform vec3 iChannelResolution[4]; // 通道分辨率(像素)
uniform vec4 iMouse; // 鼠标位置
uniform samplerXX iChannel0..3; // 输入通道 XX = 2D/Cube
uniform vec4 iDate; // 日期(年,月,日,时)
uniform float iSampleRate; // 声音采样率 (i.e., 44100)
void mainImage(){} //main函数
周期性红色闪烁
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// 将像素位置映射到0-1
vec2 uv = fragCoord.xy/iResolution.xy;// [0, 1]
// 获取通道0纹理在uv出的像素颜色
fragColor = texture(iChannel0, uv);// 黑色
// 让红色分量的值随时间改变。
fragColor.r = abs(sin(iTime));
}
当编写着色器代码时,可以考虑以下性能优化的小技巧,以最大程度地提高着色器的执行效率:
- 使用向量化操作: 在处理多个像素或顶点时,使用向量和矩阵运算可以显著提高性能。例如,使用矩阵乘法代替逐个元素的计算,使用向量的分量操作代替多次独立的计算。
- 避免频繁的纹理采样: 纹理采样是昂贵的操作,尽量减少不必要的纹理采样。可以将多个纹理采样合并为一个,使用缓存或者预计算一些值来减少采样次数。
- 减少分支和条件判断: 使用
step、mix、smoothstep等函数来代替复杂的if语句。这些函数在GPU上更容易进行优化,避免了分支预测的问题。 - 优化循环: 循环次数应该是常量或者与像素数无关的,避免动态循环次数。在可能的情况下,使用循环展开和向量化操作。
- 使用位运算: 对于一些位操作,可以使用位运算符来代替算术运算符,例如使用
&、|、<<、>>等。 - 避免重复计算: 将可能的重复计算结果存储在变量中,避免多次计算相同的值。
- 使用常量: 在需要的地方使用常量,避免动态计算常量值。
- 尽量使用浮点数运算: GPU上浮点数运算的性能通常较高,尽量使用浮点数运算而不是整数运算。
- 使用纹理压缩: 如果可以,使用纹理压缩来减少带宽和内存使用。
- 合并操作: 尽量合并多个操作,减少内存读写次数和计算次数。
- 使用合适的数据类型: 使用适当的数据类型,避免不必要的精度和开销。
- 避免递归: GPU不擅长处理递归,避免在着色器中使用递归。
- 利用预处理: 使用预处理指令(如
#define)来避免重复代码,提高代码的可维护性。
1-1 把 UV 坐标打印出来
// 建立裁剪坐标系
vec2 ClipCoord(in vec2 fragCoord) {
return (fragCoord / iResolution.xy ); // [0, 1]
}
// 主渲染函数,输出渲染结果
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 coord = ClipCoord(fragCoord);
fragColor = vec4(coord, 0, 1);
}
// UV 坐标,左下角为(0,0) 右上角为(1,1)
//
1-2 将坐标原点从左下角移动到右上角
// 建立裁剪坐标系
vec2 ClipCoord(in vec2 fragCoord) {
return 2. * (fragCoord / iResolution.xy - 0.5); // [-1, 1]
}
// 主渲染函数,输出渲染结果
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 coord = ClipCoord(fragCoord);
fragColor = vec4(coord, 1, 1);
}
1-3 绘制圆形
// 建立裁剪坐标系
vec2 ClipCoord(in vec2 fragCoord) {
return 2. * (fragCoord / iResolution.xy - 0.5); // [-1, 1]
}
// 主渲染函数,输出渲染结果
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 coord = ClipCoord(fragCoord);// 坐标变换
float coordlen = length(coord);//
fragColor = vec4(vec3(coordlen), 1);
}
// 为什么是椭圆?
1-4 绘制实心圆形
// 建立裁剪坐标系
vec2 ClipCoord(in vec2 fragCoord) {
return 2. * (fragCoord / iResolution.xy - 0.5); // [-1, 1]
}
// 主渲染函数,输出渲染结果
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 coord = ClipCoord(fragCoord);
float coordlen = length(coord);
vec3 rgb = vec3(step(0.1, coordlen));
fragColor = vec4(rgb, 1);
}
1-5 绘制真正的圆形
// 建立投影坐标系
vec2 ProjectionCoord(in vec2 fragCoord) {
return 2. * (fragCoord.xy - 0.5 * iResolution.xy) / min(iResolution.x, iResolution.y);// 标准化纵向的最大距离是1
}
// 主渲染函数,输出渲染结果
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 coord = ProjectionCoord(fragCoord);// 坐标变换,使得物体被投影到1*1的中心区域
float coordlen = length(coord);// 求长度
vec3 rgb = vec3(step(1.0, coordlen) * vec3(1, 0, 1));// 根据长度来染色
fragColor = vec4(rgb, 1);
}