Unity3d绘制标量场云图/Contour Map/Fringe

来源:互联网 发布:linux execl函数作用 编辑:程序博客网 时间:2024/05/21 02:49

名词约定:

云图/Contour Map/Fringe——CFD/FEM名词,用颜色来表示某个节点(Node)或者网格(Cell)上的物理量。就像下图这样:

Grid——CFD/FEM名词,用来对物体/场等进行离散的网格;

Cell——CFD/FEM名词,Grid的最小单位,单个网格,常见的三维Cell有六面体、四面体,二维Cell有四边形、三角形;

Node——CFD/FEM名词,Cell的顶点,称为节点;

Mesh——计算机图形学名词,Unity3d/3dMax等用来进行三维建模的三角形网格;

Triangle——计算机图形学名词,Mesh的最小单位,单个三角形面片;

Vertice——计算机图形学名词,Triangle的顶点;

正文:
最近遇到个需求,就是用Unity来展示空间内的物理量,需要绘制标量场的云图。
网上找了找别人有的方案是写shader,网友御雪飞舞给我提供的方案是动态生成纹理图片,这两个方案应用前提是物理场可以由简单函数描述出来(shader一共传递不了几个参数,动态生成纹理也是事先给了分布函数)。
但是实际工程中的标量场复杂的多,不可能用简单函数来描述。比如说CFD计算结果是给出一堆离散点位置上的物理量的值。翻了翻乐乐同学的Unity shader入门,我发现了较为简单、CPU压力也低(因为活基本都丢给了GPU)的方案,概括起来就是:
指定一张颜色纹理,将要绘制的云图平面离散成Grid,按照Nodes坐标复制Vertices构建Mesh,根据Node的物理量计算对应的Vertice在纹理图片中的uv值,把uv值传给每一个Triangle进行渲染。

听起来像天书,实际操作一下很简单。
1: 指定颜色纹理
拿PhotoShop做了一张图片,导入Unity3d。


2:将要绘制的云图平面离散成Grid,按照Nodes坐标复制Vertices构建Mesh
我希望绘制两个声源产生的直达声声压级的云图,于是按照下图建模。黄色小方块是声源,一个距地面2.6m,一个距离地面2.2m,云图平面位于距地面1.5m处,直接用的Unity提供的Plane。


3:根据Node的物理量计算对应的Vertice在纹理图片中的uv值,把uv值传给每一个Triangle进行渲染。
给Plane挂脚本,传三个参数进去:ColorMapUpperLimit(色谱图上限值),ColorMapLowerLimit(色谱图下限值),ColorMap(色谱图纹理)。

核心功能代码如下,利用for循环遍历Mesh中每一个Vertice,前面是根据Vertice的全局坐标计算当地A加权声压,结果保存在float变量_sp中,从倒数第5行开始根据_sp计算纹理的uv值

public void DisplayDirectSoundPressure(){        Mesh _mesh = GetComponent<MeshFilter> ().mesh;        Matrix4x4 L_W_M = transform.localToWorldMatrix;        Vector3[] _vertices = _mesh.vertices;        int _count = _mesh.vertexCount;        Vector2[] _uvs = new Vector2[_count];        for (int i = 0; i < _count; i++) {            Vector3 worldc = L_W_M.MultiplyVector (_vertices [i]);            AcousticSound _s = new AcousticSound ();            foreach (AcousticSource item in _sources) {                _s += item.DirectSound (worldc);            }            float _sp = (float)_s.AWeightSoundPressure;             float u = (_sp - ColorMapLowerLimit) / (ColorMapUpperLimit - ColorMapLowerLimit);            _uvs [i] = new Vector2 (u, 0);        }        _mesh.uv = _uvs;    } 

纹理图片是从左到右的,只需要用线性插值计算u值,v值无关紧要随便给一个
float u = (_sp - ColorMapLowerLimit) / (ColorMapUpperLimit - ColorMapLowerLimit);
然后把uv值存进去
_uvs [i] = new Vector2 (u, 0);
最后把uv数组赋给mesh
 _mesh.uv = _uvs;
最终效果:


原创粉丝点击