標籤:

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);}

得到噪音圖:

噪音圖(修正1)

修正 2

假如不想指定 highp 精度,就不能乘以太大的數字,將原始數字 43758.5453123 換成 758.545。

float rand(vec2 co) { return fract(sin(dot(co.xy,vec2(12.9898,78.233))) * 758.545);}

得到噪音圖:

噪音圖(修正2)

雖然還不夠隨機,但比修正前一半黑線要好,某些場合夠用。

移植時其它注意事項

  • 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 |