在捕鱼游戏项目中
希望使用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还是会出现明显抖动
法线参数
参数基于片段的纹理坐标,并通过时间参数设置偏移
以上所设置的参数,会带来较大的横向波动和少量的纵向波动,适用于横版游戏
反射计算
首先将纹理坐标从0~1转换为-1~1的数值
然后根据假设的眼睛位置,计算光线方向
这里inVec与z轴的夹角是很小的
因为p的范围是(-1,-1)~(1,1)
因此inVec与z轴的夹角范围应该是0~8角度atan(1.414/10)
注意normalize会把方向向量变换为 单位向量
根据法线计算折射向量
这个向量会比0~8度还要小,物理学没记错的话应该是0~8*0.7左右
而refractVec的值因为是从单位向量inVec计算得来的,因此还是单位向量
大概范围是(+-0.05 , +-0.05 , 0.9+)这样
最后处理纹理采样坐标,做偏移处理,可以看出来偏移的量很少,0.05的话也就是1/20
而且还乘了0.5
当法线始终竖直向上(0,0,1)时,可以比较一下原图和使用反射后的区别
可以明显看到,使用了折射之后,左右边缘部分gl.CLAMP掉了(上下其实也会有,没有在显示区域内)
正是因为折射偏移的影响
而在同时使用法线贴图和时间变换后
图片会展现出良好的动态波动效果
当然,最后要记得把左右边缘像上下底边一样,不要处于显示区域内部
控制时间参数的变化可以明显改变波动速度,操作十分简单方便
目前设置法线数据可以使用以下api
参考文献
http://www.cocoachina.com/bbs/read.php?tid-1693873-page-1.html