OpenGL ES 的精度問題
隨機函數
我將一些效果從 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。
推薦閱讀:
※OpenGL滑鼠操作
※圖形渲染中關於CPU和GPU的一些問題?
※求實時渲染基本演算法好書推薦?
※unity 最後一次drawcall 如何避免?
※不同GLSL版本之間的區別?
TAG:OpenGL |