这是我学习shader的一个练手项目
实现倒水动作,核心问题有三个。
- 杯子倾斜过程中,水面要保持水平
- 水要根据颜色分层
- 倒水的时候第一层水面要荡漾起来
对于问题一,旋转的过程中要保证水的体积不变,我是假定瓶子是个矩形(不考虑圆底),根据瓶子水量和瓶子倾斜角度,计算出水面的中点,然后将纹理坐标绕这个中点进行旋转,然后丢弃y>0的像素。
计算中点需要区分高度和倾斜角的几种情况,要判断水面有没有触及瓶口以及瓶底,再根据三角关系算出体积不变的情况下,对某一倾斜角度水面的中点
float ratio = iResolution.y/iResolution.x;
bool toLeft = sin(angle)>=0.0;
vec2 center = vec2(0.5,1.0-_height);//水面倾斜时,以哪个店为中心点
float _t = abs(tan(angle));
if(_height<0.5){//水的体积小于杯子的一半,先碰到下瓶底
bool is_bottom = _t/ratio>2.0*_height;//倾斜角度达到瓶底
if(is_bottom){
center.x = sqrt(2.0*_height/_t*ratio)/2.0;
center.y = 1.0 - sqrt(2.0*_height*_t/ratio)/2.0;
bool is_top = _t>(ratio)/(_height*2.0);//倾斜角度达到瓶口
if(is_top){
center.y = 0.5;
center.x = _height;
}
}
if(!toLeft){
center.x = 1.0-center.x;
}
}else{//水比较多,先碰到上瓶底
bool is_top = _t>2.0*ratio*(1.0-_height);
if(is_top){
center.x = sqrt(2.0*ratio*(1.0-_height)/_t)/2.0;
center.y = sqrt(2.0*ratio*(1.0-_height)*_t)/2.0/ratio;
bool is_bottom = _t>ratio/(2.0*(1.0-_height));
if(is_bottom){
center.y = 0.5;
center.x = 1.0-_height;
}
}
if(toLeft){
center.x = 1.0-center.x;
}
}
uv.y = uv.y*ratio;
uv -= vec2(center.x,center.y*ratio);
vec2 uv1 = tranPt(uv,angle,vec2(0.0));
对于问题2,是传入颜色、高度数组,从最下面颜色开始遍历,使用不同的高度进行绘制,如果绘制成功就跳出循环。
for(int i=0;i<MAX_ARR_LEN;i++){
if(heights[i].x<0.001){
continue;
}
_height+=heights[i].x;
a += drawWater(uv,angle,_height,size,i);
if(a>0.0){//绘制过的,跳过
ret *= a*colors[i];
break;
}
}
水面荡漾,我使用的是最简单的三角函数,还不是很自然,怎样模拟自然的水面荡漾效果,还有待学习。
float y = 0.0;
bool hasWave = curIdx==arrSize-1;//只有最上面一层有波浪
hasWave = hasWave;
if(hasWave){
// 代入正弦曲线公式计算模拟水面波浪 y = Asin(ωx ± φ)
float amplitude = 0.0;// 振幅(控制波浪顶端和底端的高度)
float angularVelocity = 0.0;// 角速度(控制波浪的周期)
float frequency = 0.0;// 频率(控制波浪移动的速度)
if(abs(waveType-1.0)<0.01){//往里倒水
amplitude = 0.06;
angularVelocity = 10.0;
frequency = 10.0;
}else if(abs(waveType-2.0)<0.01){//往外倒水
amplitude = 0.03;
angularVelocity = 5.0;
frequency = 6.0;
}
y = amplitude * sin((angularVelocity * uv1.x) + (frequency * cc_time.x)*(toLeft ? 1. : -1.));
}
倾倒的水流是用的cc.Graphics组件。
倒水的过程其实就是不停的减少水面高度、修改杯子倾斜度。
倒水完整源码,已经发布到 cocosstore,完成度80%的小游戏