這是我學(xué)習(xí)shader的一個練手項目
倒水.gif
實現(xiàn)倒水動作灵汪,核心問題有三個戒傻。
- 杯子傾斜過程中脑奠,水面要保持水平
- 水要根據(jù)顏色分層
- 倒水的時候第一層水面要蕩漾起來
對于問題一,旋轉(zhuǎn)的過程中要保證水的體積不變赖钞,我是假定瓶子是個矩形(不考慮圓底),根據(jù)瓶子水量和瓶子傾斜角度聘裁,計算出水面的中點(diǎn)雪营,然后將紋理坐標(biāo)繞這個中點(diǎn)進(jìn)行旋轉(zhuǎn),然后丟棄y>0的像素衡便。
計算中點(diǎn)需要區(qū)分高度和傾斜角的幾種情況献起,要判斷水面有沒有觸及瓶口以及瓶底洋访,再根據(jù)三角關(guān)系算出體積不變的情況下,對某一傾斜角度水面的中點(diǎn)
float ratio = iResolution.y/iResolution.x;
bool toLeft = sin(angle)>=0.0;
vec2 center = vec2(0.5,1.0-_height);//水面傾斜時谴餐,以哪個店為中心點(diǎn)
float _t = abs(tan(angle));
if(_height<0.5){//水的體積小于杯子的一半,先碰到下瓶底
bool is_bottom = _t/ratio>2.0*_height;//傾斜角度達(dá)到瓶底
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);//傾斜角度達(dá)到瓶口
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,是傳入顏色岂嗓、高度數(shù)組汁展,從最下面顏色開始遍歷,使用不同的高度進(jìn)行繪制厌殉,如果繪制成功就跳出循環(huán)食绿。
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;
}
}
水面蕩漾公罕,我使用的是最簡單的三角函數(shù)器紧,還不是很自然,怎樣模擬自然的水面蕩漾效果楼眷,還有待學(xué)習(xí)铲汪。
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組件。
倒水的過程其實就是不停的減少水面高度罐柳、修改杯子傾斜度掌腰。
倒水小游戲完整源碼,已經(jīng)發(fā)布到 cocosstore