动态效果如下:

之前写过一个模拟血瓶效果的文章。

Lucifer:在Unity中完善一下Unreal模拟伪液体血瓶的效果zhuanlan.zhihu.com

那篇主要是为了模拟正确的液面,好进行比较正确的反射和投影等效果。这次的液体,从基本原理上来说,实现思路应该差不多。不过这次,因为瓶子已经不是规则形状了,所以,继续计算正确的液面应该就不现实了。这次还要配合瓶子的运动状态,液体的形态也要做出相应的正确改变。

先从实现思路上捋一下基本实现步骤:

1、内部液体的基本呈现:液体是要配合瓶子形状的,所以基本的实现思路就是将瓶子双Pass渲染,先将瓶子进行必要的顶点缩放,渲染内部液体,然后再渲染瓶子本身。

2、瓶内液面的位置设定:液体因为受重力影响,所以液面也总是会垂直于重力方向。所以对液体进行裁剪时,需要按照世界空间的高度进行裁剪。

所以其他先不考虑的情况下,我们可以先使用Shader,将上面两步实现出来。

一、建模,打开3dsMax软件(༼ つ ◕_◕ ༽つ程序员来学Max建模了)。

1、alt-W,将透视图(默认软件最右下角视图)最大化。

2、基本视图快捷键操作和基本建模操作。

视图:z,最大化视图。

p,f,t,l,透,前,顶,左视图。

鼠标中键,左右平移视图。

alt+鼠标中键,以选中物体为重心旋转视图。

鼠标滚轮:缩放视图。

操作:左键点选物体。

q、w、e、r,选取,移动,旋转,缩放物体。

鼠标右键:悬浮菜单。

3、创建一个简单的柱体。

依次在右侧主工具栏选择创建、模型、标准原型、柱体。

鼠标变为下图符号后:


在主视图左键按住,并拖动鼠标,创建圆片。然后松手,继续拖动鼠标拉出高度,等高度合适后,再次点击左键,创建出圆柱体。点击右键,取消创建命令。

4、调节圆柱体参数。

左键点击选中柱体,按F4,显示模型网格。移动模型到0,0,0位置。并在右侧修改菜单中,设置柱体的分段参数。如图:


调节完毕之后,右键将模型转为可编辑多边形。


5、造瓶体

建模方式有很多,我按照我习惯来说:

选择模型顶上的面,delete,删除。

顶面删除

选择体,并选择模型,进行一下缩放到符合一般酒瓶子的宽高比例。

选择整个体

然后选择上面的封闭线,按住shift,左键向上拉出并将拉出的边做一下缩放。

按住shift拉出

缩放一下,来制作酒瓶上方较窄的形状

重复以上步骤,直到拉出以下形体。

中途如果需要调整,可以切换到顶点模式,按F3,仅显示网格,并选中需要调整的顶点进行调整

6、优化瓶口

不讲究的话,上面的瓶子就能导出成Fbx使用了,如果想做好点,就继续优化一下瓶口,同样使用上面的方法,调整成下面的形状。

就是不断的按住shift网上拉面,调整距离和缩放

7、导出Fbx

养成良好习惯,导出前,重置 一下模型的矩阵信息。

选择菜单、导出,并选择Fbx格式,导出模型即可。

Fbx导出设置这里请参考下图:

这样即可,其他设置在这里并不是重点。

下面提供一下测试用模型(我知道你们第一次建模,结果肯定惨不忍睹,就一个字:丑(●'◡'●)):

https://pan.baidu.com/link/zhihu/7thWzOuehUiWTQxmpFWv5BFWVmT5BjaQQ5lF==

二、Shader

按上面的思路,就是液体,瓶子渲染两遍就好了,没啥特别的。

Shader "Unlit/BottleLiquid_Test"{  Properties    {     _LiquidColor ("LiquidColor", Color) = (0.2,0.1,0.1,0.9)        _BottleColor("BottleColor", Color) = (0.55,0.95,0.45,0.5)  } SubShader {     Tags { "RenderType"="Transparent" "Queue" = "Transparent"}      LOD 100

     Pass//第一个pass先渲染瓶中的液体       {         Blend SrcAlpha OneMinusSrcAlpha//这里要双面渲染液体            Cull off          CGPROGRAM#pragma vertex vert          #pragma fragment frag                       #include "UnityCG.cginc"

struct appdata          {             float4 vertex : POSITION;         };

struct v2f          {             float4 vertex : SV_POSITION;              };

          float4 _LiquidColor;

            v2f vert (appdata v)          {             v2f o;//这里就是对瓶子进行缩放,比例决定瓶子的厚度,后面加了一个向上的小偏移,因为瓶底儿应该更厚点儿。              float4 localPos = float4(0.9, 0.9, 0.98, 1) * v.vertex + float4(0, 0, 0.01, 0);             o.vertex = UnityObjectToClipPos(localPos);return o;            }

           fixed4 frag(v2f i) : SV_Target            {return _LiquidColor;           }         ENDCG     }

       Pass      {         Blend SrcAlpha OneMinusSrcAlpha           CGPROGRAM#pragma vertex vert          #pragma fragment frag           #include "UnityCG.cginc"

struct appdata          {             float4 vertex : POSITION;         };

struct v2f          {             float4 vertex : SV_POSITION;          };

          float4 _BottleColor;

            v2f vert(appdata v)           {             v2f o;                o.vertex = UnityObjectToClipPos(v.vertex);return o;            }

           fixed4 frag(v2f i) : SV_Target            {return _BottleColor;           }         ENDCG     } }}

然后我们就得到了:

嗯,然后下一步就是控制液体的液面高度了。

猛地一想,貌似只要把高度超过一个定值的液体剪裁掉就可以了。

所以我们可以猛地先这么一写:

Shader中:

//增加属性液面高度_WaterLevel_WaterLevel ("WaterLevel", range(0.1, 1.5)) = 1.0

struct v2f{    float4 vertex : SV_POSITION;//顶点输出中增加世界空间坐标    float4 worldPos : TEXCOORD0;};

float _WaterLevel;

//VS中增加关于世界坐标的计算o.worldPos = mul(unity_ObjectToWorld, localPos);

//PS中增加液体剪裁,高于设置的高度的液体就直接裁剪掉clip(_WaterLevel- i.worldPos.y);

嗯,调一下Level貌似有效:

但是,移动瓶子的时候,液体不跟着瓶子运动啊。。。。

十年之前,我不认识你,你不属于我 ( ̄_, ̄ )

呃,好吧,还需要考虑瓶子本身的世界坐标,然后根据高度差来决定液面高度。

我们先这么猛地改一下试试:

//改写VS中获取世界坐标的计算,用来获取坐标差//o.worldPos = mul(unity_ObjectToWorld, localPos);o.worldPos = mul(unity_ObjectToWorld, localPos) - mul(unity_ObjectToWorld, float4(0,0,0,1));

哎,貌似可以了哎:

但是,一旋转就露馅了:

瓶子里面的液体不能保持体积啊。本来半瓶的液体,瓶子横过来,液体就满了。要是现实中的快乐水能这样,那真是ƪ(˘⌣˘)ʃ。

三、体积

好吧,终于还是到了这一步了,我们怎么才能让液体保持一个相对稳定的体积呢?瓶子可是不规则形状的,明显不能根据瓶子所处的状态,来硬性计算液面的相对高度啊。

那,如果把瓶子近似成一个规则形状呢,比如box?那我们在瓶子上加一个boxcollider试试?


添加Box Collider组件,然后调整一下刚好套住瓶子。这里为什么我把collider往下面调了一下,低于瓶口了呢?原因你们自己考虑,哈哈。

然后我们就可以根据BoxCollider的8个顶点坐标,来求某个体积的液体的液面了:


可是,这么想想,好复杂啊。能不能再简化一下啊?

最终简化:

直接通过根据collider的8个顶点的最高最低点的高度插值,来确定液体的液面高度。

嗯嗯,OK,就这么搞。

创建C#脚本,用来获取box collider的顶点,并将最高最低点坐标传入shader:

using System.Collections;using System.Collections.Generic;using UnityEngine;[RequireComponent(typeof(BoxCollider))]public class BoundingBox : MonoBehaviour {

private Material bottleLiquidMat;private BoxCollider bottleBox;//用来存储8个顶点的Local坐标    private Vector4[] localPos = new Vector4[8];

//x_最大值,y_最小值    private Vector3 volum;static readonly int _Volum = Shader.PropertyToID("_Volum");

//获取collider的顶点    void initializeVolum()    {   volum = Vector3.zero;    bottleBox = GetComponent();  Vector4 centerPos = bottleBox.center;    centerPos.w = 1.0f;  Vector4 boxSize = bottleBox.size;    boxSize.w = 0.0f;    localPos[0] = centerPos + 0.5f * boxSize;   localPos[1] = centerPos - 0.5f * boxSize;    localPos[2] = centerPos + 0.5f * new Vector4(-boxSize.x, boxSize.y, boxSize.z, boxSize.w);  localPos[3] = centerPos + 0.5f * new Vector4(-boxSize.x, -boxSize.y, boxSize.z, boxSize.w); localPos[4] = centerPos + 0.5f * new Vector4(-boxSize.x, boxSize.y, -boxSize.z, boxSize.w); localPos[5] = centerPos + 0.5f * new Vector4(boxSize.x, -boxSize.y, boxSize.z, boxSize.w);  localPos[6] = centerPos + 0.5f * new Vector4(boxSize.x, -boxSize.y, -boxSize.z, boxSize.w); localPos[7] = centerPos + 0.5f * new Vector4(boxSize.x, boxSize.y, -boxSize.z, boxSize.w);    }//计算collider的最低和最高的顶点世界坐标    Vector3 calculateVolum()    {   Vector3 Volum;    Matrix4x4 localToWorld = transform.localToWorldMatrix;   Volum.x = -9999999;  Volum.y = 9999999;   Vector4 worldPos;//判断一下最高最低点    for (int i = 0; i < 8; i++) {     worldPos = localToWorld * localPos[i];if (worldPos.y > Volum.x)           Volum.x = worldPos.y;if (worldPos.y < Volum.y)            Volum.y = worldPos.y;    } Volum.z = 0;return Volum;    }//初始和结束都将体积计算并赋值一下。    private void OnEnable()    {    bottleLiquidMat = GetComponent().material;if (bottleLiquidMat.HasProperty("_Volum"))   {     initializeVolum();        volum = calculateVolum();        bottleLiquidMat.SetVector(_Volum, volum); }    }private void OnDisable()    {if (bottleLiquidMat.HasProperty("_Volum"))   {     initializeVolum();        volum = calculateVolum();        bottleLiquidMat.SetVector(_Volum, volum); }    }//每帧判断瓶子是否移动了,重新给shader传入最高最低点    void Update () {if (transform.hasChanged && bottleLiquidMat.HasProperty("_Volum")) {     volum = calculateVolum();        bottleLiquidMat.SetVector(_Volum, volum);     transform.hasChanged = false;    }           }}

然后把脚本直接丢到瓶子身上。我们再在Shader中设置变量,接收一下:

//改一下level的数值范围。_WaterLevel ("WaterLevel", range(0.2, 0.9)) = 0.5

//找个地方,声明体积变量uniform float4 _Volum;

//前面都不需要修改,PS中增加一些代码fixed4 frag(v2f i) : SV_Target{//取到最高,最低点    float heightMax = _Volum.x;float heightMin = _Volum.y;//直接根据设置的液面高度,对最低和最高顶点进行插值一下,求出液面实际高度    float waterLevel = heightMin + _WaterLevel * (heightMax - heightMin);    clip(waterLevel - i.worldPos.y);return _LiquidColor;}

OK,到这里,瓶中液体的液面就能根据液体体积进行自动适应了。运行看一下,是不是像下面一样了?

快乐水横竖都不会变多了,没有快乐了 ̄へ ̄

嗯,然后就是最后一步,加入液面的摇晃就完成了。先到这里,休息休息一下。

unity 获取鼠标点击位置_Unity中实现瓶中液体晃动的效果(从建模开始)一相关推荐

  1. (一)MFC读取并显示一幅位图图像,并获取鼠标点击位置的像素坐标和灰度值(接上篇博客)

    上篇博客简要介绍了如何利用MFC读取并显示一幅位图图像,并获取鼠标点击位置的像素坐标和灰度值信息,主要包含了对话框的创建和添加程序,代码也在上一页中给了大家,但是并没有详细说明功能,所以在这一节当中主 ...

  2. Unity 获取鼠标点击图片时 获取点击位置的像素

    脚本要求: 需要获取颜色的图片以走下角为起点建立空物体 右上角建立空物体(两个空物体均设置为图片的子物体,设置好锚点,将坐标改为0即可).建立好碰撞体(BoxCollider).用2DBoxColli ...

  3. cesium获取点击内容信息_Cesium获取鼠标点击位置(PickPosition)

    cesium学习了这么长时间,有时候写鼠标点击事件时,想获取鼠标点击点位置,发现情况很多.比如以下情形: 1获取鼠标点的对应椭球面位置 2获取加载地形后对应的经纬度和高程 3获取倾斜摄影或模型点击处的 ...

  4. Unity 获取鼠标点击转换世界坐标

    /// <summary> /// 获取鼠标点击坐标 /// </summary> Vector3 screenPosition;//将物体从世界坐标转换为屏幕坐标 Vecto ...

  5. (一)MFC读取并显示一幅位图图像,并获取鼠标点击位置的像素坐标和灰度值

    题目是老师布置的一道作业题,要求用C或C++完成,但不能用VTK/Opencv等软件包,经过很多摸索之后实现了该功能,后续可能还有其他功能要实现,所以先写一篇博客记录下,一方面是方便自己以后使用,另一 ...

  6. 获取鼠标点击位置的坐标

    获取点击的坐标有三种情况 获取在可视区域的坐标 获取在文档页面的坐标 获取在电脑屏幕的坐标 一:获取在可视区域的坐标 e.clientX  : 获取距离页面左边的距离 e.clientY :距离页面可 ...

  7. html获取鼠标的当前位置

    html获取鼠标的当前位置 1.相对于屏幕 如果是涉及到鼠标点击确定位置相对比较简单,获取到鼠标点击事件后,事件screenX,screenY获取的是点击位置相对于屏幕的左边距与上边距,不考虑ifra ...

  8. js获取鼠标点击坐标

    转载自:http://www.cnblogs.com/dolphinX/archive/2012/10/09/2717119.html 在一些DOM操作中我们经常会跟元素的位置打交道,鼠标交互式一个经 ...

  9. 根据鼠标点击位置获取DataGridView的选择行号。

    一个非常非常郁闷的问题:在DataGridView中当右键点击某一行的时候才显示右键菜单. 找了好几个方法结果总是回归到对鼠标点击位置的判断,用鼠标点击位置来判断点中的是哪一行. 找了好几个函数都没有 ...

  10. JS获取页面鼠标点击位置的坐标

    本来想通过JS实现当前页面对其他页面的操作,在网上发现了这段js代码,先保存下来,可以获取页面鼠标点击位置的坐标. <html> <body> <script> f ...

最新文章

  1. 用CMake构建工程时 cmake -G“Unix Makefiles“ 的使用
  2. EntityManager:seam新手必读(二)
  3. 给ztree节点赋值
  4. Java操作shell脚本
  5. Windows Phone 8.1 应用商店将于 12 月 16 日关闭
  6. python读取多行函数_Python3基础 __doc__ 单行与多行函数文档
  7. gdb官方说明文档中文版
  8. python生成手写文字图片_使用PHP辅助 快速制作一套自己的手写字体实践
  9. java reader类子类_Java之InputStreamReader类的实现
  10. Linux内核4.4 init,linux4.4内核启动到INIT: version 2.88 booting 卡住
  11. 车机芯片:今后买车就像从前配电脑
  12. oracle的ofs,windows2003+oracle ofs 双机
  13. 浅谈JavaScript、ES5、ES6 ,,转自http://www.cnblogs.com/lovesong/p/4908871.html
  14. オルレイア / 雷斧
  15. bug记录-socket hang up
  16. 【Android】Android中如何取消调转界面后EditText默认获取聚焦问题
  17. 药到痰出——喉咙不再有痰
  18. Qimage颜色显示反色总结
  19. mdb数据库连接代码_重用与MDB2的现有数据库连接
  20. Java面试与职业生涯规划

热门文章

  1. from scipy.misc import imread 报错原因
  2. bzoj2616:SPOJ PERIODNI
  3. 软件工程过程 - 期末复习
  4. kill -3 获取threaddump信息
  5. 汇编程序16位带符号变量计算
  6. 智能优化算法:乌鸦搜索算法-附代码
  7. 基于C#和DGAL包实现栅格影像的读取和显示
  8. python地理数据可视化
  9. 模板题——贪心(1)
  10. arcpy 验证中心点是否位于图层之内