CocosCreator水波折射效果shader


在捕魚游戲項目中

希望使用shader豐富海洋背景效果

在網上找了幾個不同的效果

本文就寫一下使用折射效果的體會

效果視頻http://v.douyin.com/JfVfmU/

轉載請注明出處https://www.cnblogs.com/billyrun/articles/9222345.html

 

shader代碼如下

#ifdef GL_ES
precision mediump float;
#endif

uniform float time;
varying vec2 v_texCoord;
uniform sampler2D u_normalMap; 
varying vec4 v_fragmentColor;

vec3 waveNormal(vec2 p) { 
    // 避免法線圖邊緣穿幫
    // fract返回小數部分 mod取模
    // 0.1 0.2 ... 0.9 1.0 0.9 ... 0.1 0.0 0.1 0.2 ...
    float x = mod(p.x , 2.0) < 1.0 ? fract(p.x) : 1.0 - fract(p.x);
    float y = mod(p.y , 2.0) < 1.0 ? fract(p.y) : 1.0 - fract(p.y);
    //vec3 normal = texture2D(u_normalMap, p).xyz;  
    vec3 normal = texture2D(u_normalMap, vec2(x , y)).xyz;  
    normal = -1.0 + normal * 2.0;  
    normal.x *= 0.1;
    normal.y *= 0.1;
    return normalize(normal);  
    //return vec3(0,0,1);
}  

void main() {  
    float timeFactor = 0.1;  
    float offsetFactor = 0.5;  
    float refractionFactor = 0.7;  
    
    // simple UV animation  
    vec3 normal = waveNormal(v_texCoord + vec2(time * 1.0 * timeFactor, time * 0.1 * timeFactor));  
    
    // simple calculate refraction UV offset  
    vec2 p = -1.0 + 2.0 * v_texCoord;  
    // 眼睛位置 位於中心點正上方 
    vec3 eyePos = vec3(0, 0, 10);  
    vec3 inVec = normalize(vec3(p, 0) - eyePos);  
    vec3 refractVec = refract(inVec, normal, refractionFactor);  //根據入射向量,法線,折射系數計算折射向量
    vec2 v_texCoordN = v_texCoord;
    v_texCoordN += refractVec.xy * offsetFactor;    
    //v_texCoordN.x -= CC_Time.y*timeFactor *0.6; //移動水面貼圖,可選
    
    gl_FragColor = texture2D(CC_Texture0, v_texCoordN);
} 

 

法線獲取函數

waveNormal函數返回每個紋理坐標所對應的法線

假設水面是純平的,那么每一處法線都豎直向上,即vec3(0,0,1)

為了達到較為逼真的效果,水面應該是起伏不平的

這里使用一張法線貼圖來提供數據

圖片每個像素的RGB值被用作法線坐標

圖片是藍色的,RGB約為0.1,0.1,1.0,對應法線坐標xyz就是基本豎直向上但略有波動

所以也不難理解

waveNormal中做了部分數值改動

x和y分量分別*=0.1是因為使用的紋理圖RG的值有點偏大,對應xy波動太劇烈

取模求余mod和fract保留小數部分是為了讓法線采樣坐標平滑的保持在0~1之間

避免穿幫的波動

因為雖然法線貼圖的采樣會使用gl.REPEAT,但從1.0增到0.0還是會出現明顯抖動

 

法線參數

vec3 normal = waveNormal(v_texCoord + vec2(time * 1.0 * timeFactor, time * 0.1 * timeFactor));

參數基於片段的紋理坐標,並通過時間參數設置偏移

以上所設置的參數,會帶來較大的橫向波動和少量的縱向波動,適用於橫版游戲

 

反射計算

首先將紋理坐標從0~1轉換為-1~1的數值

vec2 p = -1.0 + 2.0 * v_texCoord;

然后根據假設的眼睛位置,計算光線方向

vec3 eyePos = vec3(0, 0, 10);
vec3 inVec = normalize(vec3(p, 0) - eyePos);

這里inVec與z軸的夾角是很小的

因為p的范圍是(-1,-1)~(1,1)

因此inVec與z軸的夾角范圍應該是0~8角度atan(1.414/10)

注意normalize會把方向向量變換為 單位向量

vec3 refractVec = refract(inVec, normal, refractionFactor);

根據法線計算折射向量

這個向量會比0~8度還要小,物理學沒記錯的話應該是0~8*0.7左右

而refractVec的值因為是從單位向量inVec計算得來的,因此還是單位向量

大概范圍是(+-0.05 , +-0.05 , 0.9+)這樣

vec2 v_texCoordN = v_texCoord;
v_texCoordN += refractVec.xy * offsetFactor;

最后處理紋理采樣坐標,做偏移處理,可以看出來偏移的量很少,0.05的話也就是1/20

而且還乘了0.5

 

當法線始終豎直向上(0,0,1)時,可以比較一下原圖和使用反射后的區別

可以明顯看到,使用了折射之后,左右邊緣部分gl.CLAMP掉了(上下其實也會有,沒有在顯示區域內)

正是因為折射偏移的影響

而在同時使用法線貼圖和時間變換后

圖片會展現出良好的動態波動效果

當然,最后要記得把左右邊緣像上下底邊一樣,不要處於顯示區域內部

控制時間參數的變化可以明顯改變波動速度,操作十分簡單方便

目前設置法線數據可以使用以下api

if (cc.sys.isNative) {
  glProgram_state.setUniformTexture('samplerNameXXX' , tex)
} else {
  var locNormalSample = program._glContext.getUniformLocation(program._programObj, 'samplerNameXXX');
  program.setUniformLocationWith1i(locNormalSample, textureUnitIndex);
  cc.gl.bindTexture2DN(textureUnitIndex, tex);
}

 

參考文獻

http://www.cocoachina.com/bbs/read.php?tid-1693873-page-1.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM