unity 获取鼠标点击位置_Unity中实现瓶中液体晃动的效果(从建模开始)一
动态效果如下:
之前写过一个模拟血瓶效果的文章。
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,左键向上拉出并将拉出的边做一下缩放。
缩放一下,来制作酒瓶上方较窄的形状
重复以上步骤,直到拉出以下形体。
6、优化瓶口
不讲究的话,上面的瓶子就能导出成Fbx使用了,如果想做好点,就继续优化一下瓶口,同样使用上面的方法,调整成下面的形状。
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中实现瓶中液体晃动的效果(从建模开始)一相关推荐
- (一)MFC读取并显示一幅位图图像,并获取鼠标点击位置的像素坐标和灰度值(接上篇博客)
上篇博客简要介绍了如何利用MFC读取并显示一幅位图图像,并获取鼠标点击位置的像素坐标和灰度值信息,主要包含了对话框的创建和添加程序,代码也在上一页中给了大家,但是并没有详细说明功能,所以在这一节当中主 ...
- Unity 获取鼠标点击图片时 获取点击位置的像素
脚本要求: 需要获取颜色的图片以走下角为起点建立空物体 右上角建立空物体(两个空物体均设置为图片的子物体,设置好锚点,将坐标改为0即可).建立好碰撞体(BoxCollider).用2DBoxColli ...
- cesium获取点击内容信息_Cesium获取鼠标点击位置(PickPosition)
cesium学习了这么长时间,有时候写鼠标点击事件时,想获取鼠标点击点位置,发现情况很多.比如以下情形: 1获取鼠标点的对应椭球面位置 2获取加载地形后对应的经纬度和高程 3获取倾斜摄影或模型点击处的 ...
- Unity 获取鼠标点击转换世界坐标
/// <summary> /// 获取鼠标点击坐标 /// </summary> Vector3 screenPosition;//将物体从世界坐标转换为屏幕坐标 Vecto ...
- (一)MFC读取并显示一幅位图图像,并获取鼠标点击位置的像素坐标和灰度值
题目是老师布置的一道作业题,要求用C或C++完成,但不能用VTK/Opencv等软件包,经过很多摸索之后实现了该功能,后续可能还有其他功能要实现,所以先写一篇博客记录下,一方面是方便自己以后使用,另一 ...
- 获取鼠标点击位置的坐标
获取点击的坐标有三种情况 获取在可视区域的坐标 获取在文档页面的坐标 获取在电脑屏幕的坐标 一:获取在可视区域的坐标 e.clientX : 获取距离页面左边的距离 e.clientY :距离页面可 ...
- html获取鼠标的当前位置
html获取鼠标的当前位置 1.相对于屏幕 如果是涉及到鼠标点击确定位置相对比较简单,获取到鼠标点击事件后,事件screenX,screenY获取的是点击位置相对于屏幕的左边距与上边距,不考虑ifra ...
- js获取鼠标点击坐标
转载自:http://www.cnblogs.com/dolphinX/archive/2012/10/09/2717119.html 在一些DOM操作中我们经常会跟元素的位置打交道,鼠标交互式一个经 ...
- 根据鼠标点击位置获取DataGridView的选择行号。
一个非常非常郁闷的问题:在DataGridView中当右键点击某一行的时候才显示右键菜单. 找了好几个方法结果总是回归到对鼠标点击位置的判断,用鼠标点击位置来判断点中的是哪一行. 找了好几个函数都没有 ...
- JS获取页面鼠标点击位置的坐标
本来想通过JS实现当前页面对其他页面的操作,在网上发现了这段js代码,先保存下来,可以获取页面鼠标点击位置的坐标. <html> <body> <script> f ...
最新文章
- 用CMake构建工程时 cmake -G“Unix Makefiles“ 的使用
- EntityManager:seam新手必读(二)
- 给ztree节点赋值
- Java操作shell脚本
- Windows Phone 8.1 应用商店将于 12 月 16 日关闭
- python读取多行函数_Python3基础 __doc__ 单行与多行函数文档
- gdb官方说明文档中文版
- python生成手写文字图片_使用PHP辅助 快速制作一套自己的手写字体实践
- java reader类子类_Java之InputStreamReader类的实现
- Linux内核4.4 init,linux4.4内核启动到INIT: version 2.88 booting 卡住
- 车机芯片:今后买车就像从前配电脑
- oracle的ofs,windows2003+oracle ofs 双机
- 浅谈JavaScript、ES5、ES6 ,,转自http://www.cnblogs.com/lovesong/p/4908871.html
- オルレイア / 雷斧
- bug记录-socket hang up
- 【Android】Android中如何取消调转界面后EditText默认获取聚焦问题
- 药到痰出——喉咙不再有痰
- Qimage颜色显示反色总结
- mdb数据库连接代码_重用与MDB2的现有数据库连接
- Java面试与职业生涯规划