shader学习之倒水小游戏

这是我学习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%的小游戏

41赞
优秀

大佬就是大佬,练手的项目都这么:ox:

1赞

我是萌新 :sweat_smile:

萌新大佬就是萌新大佬,练手的项目都这么:ox::dog:保命)

我与大佬差距好大

大佬牛逼啊

:sweat_smile:

痴迷于算法看着好舒服

厉害厉害~~~

大佬牛批!!

标记、收藏、赞三连

good mark

大佬就是大佬~

mark一下

先mark一下。。

mark 大佬

大佬就是大佬

mark~

mark!