随机函数
我将一些效果从 Windows 移植到手机上。某个效果 Windows 跟手机差别悬殊。耐心裁剪 glsl 代码,定位到此随机函数。
float rand(vec2 co) {
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453123);
}
参考这里。Random / noise functions for GLSL
正确噪音图(Shadertoy)
上述函数很常见,生成 [0, 1] 之间的随机数。在 shadertoy 粘贴下面代码
float rand(vec2 co) {
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453123);
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord/iResolution.xy;
float r = rand(uv);
fragColor = vec4(r, r, r, 1.0);
}
可生成噪音图:
错误噪音图(iPhone 7+)
在 iPhone 7+ 运行类似代码
precision mediump float;
varying vec2 vTexCoord;
float rand(vec2 co) {
return fract(sin(dot(co.xy,vec2(12.9898,78.233))) * 43758.5453123);
}
void main()
{
vec2 uv = vTexCoord;
float r = rand(uv);
gl_FragColor = vec4(r, r, r, 1.0);
}
生成了错误的噪音图,有一半数据是黑色(黑色代表 0),原因是精度不够。
精度
移植时,要注意精度。OpenGL 精度比 OpenGL ES 精度高得多。如下例子
precision mediump float;
varying vec2 vTexCoord;
void main()
{
float r = sin(1.0);
float k = abs(r - 0.8414709848079);
if (k < 0.0005) {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
} else {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
}
Windows 上运行,精度足够,显示红色。iPhone 7+ 上运行,精度不够,显示绿色。就算将 mediump 改成 highp,iPhone 7+ 照样显示绿色。
经测试,GLES 的数字在 4 位整数(十进制),4 位小数(十进制)之间,是相对安全的,当然范围越小越准确。
修正 1
添加 highp 标识
float rand(highp vec2 co) {
return fract(sin(dot(co.xy,vec2(12.9898,78.233))) * 43758.5453123);
}
得到噪音图:
修正 2
假如不想指定 highp 精度,就不能乘以太大的数字,将原始数字 43758.5453123 换成 758.545。
float rand(vec2 co) {
return fract(sin(dot(co.xy,vec2(12.9898,78.233))) * 758.545);
}
得到噪音图:
虽然还不够随机,但比修正前一半黑线要好,某些场合够用。
移植时其它注意事项
- GLES 需显式写成浮点数,如
float k = 1;
编译错误,需写成float k = 1.0;
- 注意精度,数值不能过大或者过小。如上述随机函数,想效果更加一致,应老老实实载入噪音纹理,取其像素作为随机值。
- 假如使用 kEAGLRenderingAPIOpenGLES2 创建 EAGLContext,纹理 WrapMode 为 GL_REPEAT 会有限制,高宽必须是 2 的 n 次方(如 2、4、8、16、32、64 这些数字),不然会黑屏。kEAGLRenderingAPIOpenGLES3 没有这限制,但某些机型不支持 ES3,比如 iPhone 5C。
- GLES 不支持某些函数,如 fwidth。