Unity3D】opengl 噪声渲染烟雾动画,移动出现块状怎么办

2021-02-24 13:41发布

1条回答

过出现在像新闻组这样的公共论坛上的次数来判断, 粒子系统是一个相当热点的问题。 一部分原因可能是QUAKE使用了烟雾粒子, 血溅效果和火花落下效果, 并且获得了巨大成功。

 

但是可以肯定的是, 对粒子系统的兴趣和它们的能力有些关系, 相比其它计算机图形学方法, 更能实时实现自然现象。 William Reeves早在1982到1983年就实现了粒子系统。当工作于《星际迷航Ⅱ:可汗之怒》时, 他需要寻找一种方法来为创世纪演示序列创造逼真的火焰效果。 Reeves发现传统的擅于创建平滑清晰表面的对象的建模方式, 无法达到需求。 构成这种效果的对象不是由简单的确定的曲面组成的。 这些对象, 他称之为“模糊”, 将更好的被建模为一种粒子的系统, 这种系统通过一组动态规则运作。 粒子曾一直被用来实现像烟雾和星系这样的自然效果, 但是它很难控制。 Reeves意识到通过应用一个规定粒子的系统, 他可以在保留一些创造性的控制的同时达到一种混乱的效果。 于是粒子系统就出现了。

 

它是怎么工作的?

一个基本的粒子系统可以仅由一组空间中的3维顶点组成。 不像标准的几何对象, 粒子组成的系统不是静态的。 它们经历了一个完整的生命周期。 粒子产生, 随时间变化, 然后死亡。 通过调整影响生命周期参数, 你可以创建不同类型的效果。 另一个关于粒子系统的关键点是粒子是混乱的。 也就是说, 不是有一个确定的路径, 每个粒子可以有一个更改它们行为的元素, 成为一个随机过程(一个很好的书面用语), 这个随机过程使效果看起来非常真实自然。 这个月, 我要创建一个实时粒子系统, 展示一些基本技巧以及你可以创建的迷人效果。

 

粒子

我们先来看看一个粒子需要的属性。 首先, 我需要知道粒子的位置, 因为我还想能容易对粒子反锯齿, 所以我还要存储粒子的前一个位置。 我需要知道粒子当前移动的方向。 这可以存储为一个方向向量中。 我还需要知道粒子在这个方向的移动速度, 但是速度可以通过乘法简单的结合到方向向量中。 我要渲染粒子为彩色的点, 所以我还需要知道这个粒子的当前颜色和之前颜色以便于反锯齿。 为了让颜色随时间变化, 我把每帧的颜色变化量存储起来。 我需要的最后一个信息是粒子的生命计数, 它是这个粒子在死亡之前存在的帧数目。

表1 . 粒子结构.

struct tParticle

{

tParticle*prev,*next; // 双向链表, 指向前前驱结点和后继结点

tVector pos; // 当前位置

tVector prevPos;//之前位置

tVector dir; // 当前方向和速度

int life; // 还会存在的时间

tColor color; //粒子当前颜色

tColorprevColor; //粒子之前颜色

tColordeltaColor; //颜色改变量

};

  如表1, 我为粒子定义的数据结构。 如果你希望使你的粒子更复杂, 在这里增加属性会很容易。 你可以通过一个尺寸属性来变动粒子尺寸, 通过对颜色增加一个alpha值改变透明度。 此外, 你可以通过增加变量来增加质量属性或者其它物理属性。

表2 发射器结构

struct tEmitter

{

long id; //EMITTER ID

char name[80];// EMITTER NAME

long flags; //EMITTER FLAGS

//TRANSFORMATION INFO

tVector pos; //XYZ POSITION

float yaw, yawVar;// YAW AND VARIATION

float pitch,pitchVar; // PITCH AND VARIATION

floatspeed,speedVar;

// Particle

tParticle*particle; // NULL TERMINATED LINKED LIST

inttotalParticles; // TOTAL EMITTED AT ANY TIME

intparticleCount; // TOTAL EMITTED RIGHT NOW

intemitsPerFrame, emitVar; // EMITS PER FRAME AND VARIATION

int life,lifeVar; // LIFE COUNT AND VARIATION

tColorstartColor, startColorVar; // CURRENT COLOR OF PARTICLE

tColor endColor,endColorVar; // CURRENT COLOR OF PARTICLE

// Physics

tVector force; //GLOBAL GRAVITY, WIND, ETC.

};

发射器

  粒子发射器是在系统中负责创建粒子的实体。 这是你要在一个实时3D世界中放置来创建不同效果的对象。 发射器控制粒子的数量、 大体的发射方向以及其它全局设置。 发射器的数据结构如表2. 这也是我设置我下面要说的随机过程的地方。 例如, emitNumber是每帧应该发射的粒子数目的平均值。 emitVariance是从emitNumber加上或减去的随机数。 通过调整这两个值, 你可以通过改变常数来改变显示效果, 设置为稳定流或一个更加随机的流。 计算每帧发送的粒子数的公式:particleCount = emitNumber + (emitVariance* RandomNum()); 这里RandomNum()是一个函数, 返回-1.0到1.0之间的浮点数。

  这些技巧还用来更改颜色, 方向, 速度, 以及粒子的生命周期。 颜色是一个特例, 因为我想让颜色随粒子生命周期而变化。 我用上面的方法计算了两个随机的颜色值, 然后通过生命值划分它们的区别。 这创建了颜色delta, 它被加到每帧中的每个粒子中。

  现在我需要描述粒子应该发射的方向。 我们实际上只需要描述两个相对于初始的旋转角度, 因为粒子是空间中的单个点, 并且我不考虑旋转。旋转的两个角度是y轴的旋转(θ)和相对与x轴的(Ψ) 。 这些角度通过一个随机值改变, 然后转换为一个对每个粒子的方向向量。

  产生方向向量的转换过程很容易。 它需要一些基本的3D旋转技巧和一些基本矩阵变换知识。

  一个关于y的旋转被定义为

x’ =x*cos(q) + z*sin(q);

y’ =y;

z’ = -x*sin(q) + z*cos(q)

或者, 以矩阵的形式

一个关于x的旋转是

x’ =x;

y’ =y*cos(y) - z*sin(y);

z’ = y*sin(y) + z*cos(y)

一旦这两个矩阵被合并到一个单旋转矩阵中, 我得到了下面的矩阵:

  现在因为我在金酸方向向量, 我需要用这个矩阵乘向量(0, 0, 1)。 一旦所有的0被消去, 我得到最终代码片段, 如表3. 要完成粒子运动向量, 这个最终方向向量被一个速度标量乘, 当然, 这个速度值也是随机的。

  表3 转换旋转到一个方向向量

///

// Function:RotationToDirection

// Purpose:Convert a Yaw and Pitch to a direction vector

///

voidRotationToDirection(float pitch,float yaw,tVector *direction)

{

direction->x= -sin(yaw) * cos(pitch);

direction->y= sin(pitch);

direction->z= cos(pitch) * cos(yaw);

}

/// initParticleSystem

表4 添加一个新粒子到发射器

///

// Function:addParticle

// Purpose: adda particle to an emitter

// Arguments:The emitter to add to

///

BOOLaddParticle(tEmitter *emitter)

{

/// LocalVariables ///

tParticle*particle;

tColorstart,end;

floatyaw,pitch,speed;

///

// IF THERE ISAN EMITTER AND A PARTICLE IN THE POOL

// AND I HAVEN'TEMITTED MY MAX

if (emitter !=NULL && m_ParticlePool != NULL &&

emitter->particleCount< emitter>totalParticles)

{

particle =m_ParticlePool; // THE CURRENT PARTICLE

m_ParticlePool =m_ParticlePool->next; // FIX THE POOL POINTERS

if(emitter->particle != NULL)

emitter->particle->prev= particle; // SET BACK LINK

particle->next= emitter->particle; // SET ITS NEXT POINTER

particle->prev= NULL; // IT HAS NO BACK POINTER

emitter->particle= particle; // SET IT IN THE EMITTER

particle->pos.x= 0.0f; // RELATIVE TO EMITTER BASE

particle->pos.y= 0.0f;

particle->pos.z= 0.0f;

particle->prevPos.x= 0.0f; // USED FOR ANTI ALIAS

particle->prevPos.y= 0.0f;

particle->prevPos.z= 0.0f;

// CALCULATE THESTARTING DIRECTION VECTOR

yaw =emitter->yaw + (emitter->yawVar * RandomNum());

pitch =emitter->pitch + (emitter->pitchVar * RandomNum());

// CONVERT THEROTATIONS TO A VECTOR

RotationToDirection(pitch,yaw,&particle->dir);

// MULTIPLY INTHE SPEED FACTOR

speed =emitter->speed + (emitter->speedVar * RandomNum());

particle->dir.x*= speed;

particle->dir.y*= speed;

particle->dir.z*= speed;

// CALCULATE THECOLORS

start.r =emitter->startColor.r + (emitter->startColorVar.r * RandomNum());

start.g =emitter->startColor.g + (emitter->startColorVar.g * RandomNum());

start.b =emitter->startColor.b + (emitter->startColorVar.b * RandomNum());

end.r =emitter->endColor.r + (emitter->endColorVar.r * RandomNum());

end.g =emitter->endColor.g + (emitter->endColorVar.g * RandomNum());

end.b =emitter->endColor.b + (emitter->endColorVar.b * RandomNum());

particle->color.r= start.r;

particle->color.g= start.g;

particle->color.b= start.b;

// CALCULATE THELIFE SPAN

particle->life= emitter->life + (int)((float)emitter->lifeVar * RandomNum());

// CREATE THECOLOR DELTA

particle->deltaColor.r= (end.r - start.r) / particle->life;

particle->deltaColor.g= (end.g - start.g) / particle->life;

particle->deltaColor.b= (end.b - start.b) / particle->life;

emitter->particleCount++;// A NEW PARTICLE IS BORN

return TRUE;

}

return FALSE;

}

/// addParticle///

创建一个新粒子

  为了避免大量昂贵的内存分配开销, 所有的粒子被创建在一个通用粒子池中。 我选择把粒子池实现为一个链表。 当一个粒子被发射时, 他从通用粒子池中移除, 并且插入到发射器的粒子链表中。 尽管这限制了场景中的粒子总数, 却使速度增加了很多。 通过构造双向粒子链表, 当粒子死亡时很容易移除一个粒子。

  创建一个新粒子并把它添加到发射器中的代码如表4。 它操纵对全局粒子池所有的内存管理, 还对粒子设置全部的随机值。

  我选择简单的在发射器的源 点 创建每个新粒子。 在William Reeves的SIGGRAPH文章中, 他描述了产生粒子的不同方法。 连同点源,他还描述了在球面上创建粒子的方法,以及在球体内, 在2D圆盘的表面, 在矩形的表面。 这些不同的方法将创建不同的效果, 所以你应该通过实验来找到最适合你的应用的方法。

表5 更新粒子

///

// Function:updateParticle

// Purpose:updateParticle settings

// Arguments:The particle to update and the emitter it came from

///

BOOLupdateParticle(tParticle *particle,tEmitter *emitter)

{

// IF THIS IS AVALID PARTICLE

if (particle !=NULL && particle->life > 0)

{

// SAVE ITS OLDPOS FOR ANTI ALIASING

particle->prevPos.x= particle->pos.x;

particle->prevPos.y= particle->pos.y;

particle->prevPos.z= particle->pos.z;

// CALCULATE THENEW

particle->pos.x+= particle->dir.x;

particle->pos.y+= particle->dir.y;

particle->pos.z+= particle->dir.z;

// APPLY GLOBALFORCE TO DIRECTION

particle->dir.x+= emitter->force.x;

particle->dir.y+= emitter->force.y;

particle->dir.z+= emitter->force.z;

// SAVE THE OLDCOLOR

particle->prevColor.r= particle->color.r;

particle->prevColor.g= particle->color.g;

particle->prevColor.b= particle->color.b;

// GET THE NEWCOLOR

particle->color.r+= particle->deltaColor.r;

particle->color.g+= particle->deltaColor.g;

particle->color.b+= particle->deltaColor.b;

particle->lifeÑ;//IT IS A CYCLE OLDER

return TRUE;

}

else if(particle != NULL && particle->life == 0)

{

// FREE THISSUCKER UP BACK TO THE MAIN POOL

if(particle->prev != NULL)

particle->prev->next= particle->next;

else

emitter->particle= particle->next;

// FIX UP THENEXTÕS PREV POINTER IF THERE IS A NEXT

if(particle->next != NULL)

particle->next->prev= particle->prev;

particle->next= m_ParticlePool;

m_ParticlePool =particle; // NEW POOL POINTER

emitter->particleCountÑ;// ADD ONE TO POOL

}

return FALSE;

}

/// updateParticle///

更新粒子

  一旦粒子产生了, 它就由粒子系统处理。 更新过程的代码如表5. 对每个模拟的每个生命周期, 粒子会被更新。 首先, 检查某粒子是否死亡, 如果它已经死亡, 粒子从发射器链表中移除并返回到全局粒子池中。 这时, 全局设置被应用到方向向量中, 并且颜色也被更改。

 

渲染粒子系统

  简单的看, 粒子系统就是一组点的集合, 因此它可以像一组3D彩色点来渲染。你还可以计算一个围绕点的多边形, 来使得它总是像一个广告版面对整个镜头。 然后应用任何你喜欢的纹理到这个多边形。 通过缩放多边形到镜头的距离, 你可以创建透视效果。 另一个选择是在粒子的位置绘制任何类型的3D对象。

  我采用简单的路线。 我仅仅把每个粒子绘成一个3D顶点。 如果你开启发锯齿, 系统从前一个位置和颜色到新的位置和颜色绘制一个高落德着色线。 这通过耗费一个渲染速度来使看起来平滑。 你可以在图1a和1b中看到其中的不同。 第一个图像是简单的点渲染, 第二个图像由线段组成。

 

你可以用它干什么?

  一旦你设计好了你的系统, 你可以开始构建效果。 你可以简单的构建一些效果像火焰, 喷泉, 烟花, 只需要通过简单的修改发射器的属性。 通过附加发射器到另一个对象并激活它, 你可以创建简单的烟雾拖尾或彗星的尾巴。

  你还可以通过在粒子死亡时创建新的粒子系统创建更加复杂的效果。 在《星际迷航Ⅱ》中的创世纪序列实际上拥有最多400个粒子系统包含750,000个粒子。 对于你的实时血液喷射效果来说这可能有点多, 但是随着硬件变得越来越快, 谁又知道呢?

  另外, 我的简单的物理模型可以被大幅修改。 大部分粒子可以是随机的, 引起引力使它们出现不同的效果。 一个摩擦模型会迫使一些粒子在运动时减速。 另外的近地空间效果, 像磁场, 风吹, 旋转漩涡, 会更大程度的改变粒子。或者你可以在生命周期中改变emitsPerFrame来创建烟雾膨胀的效果。

  我在一些商业粒子系统的实现中见到很多想法。 你可以随时间变动粒子的尺寸来创建一个分散效果。 在生命周期中增加更多的关键位置来创建更复杂的样子。另外一个有趣变种是用粒子系统创建植物。 通过在生命周期中跟踪每个位置, 然后渲染出一条贯穿这些点的线, 你可以得到一丛草的对象。 像这样的有机对象是难以用多边形建模的。 另外一个扩展领域是碰撞检测。 你可以创建弹射像立方体或者球体的边界对象来简单的反射一个方向向量。

  你可以从我上面描述这些想法中来探索用粒子系统可以创建什么。 通过创建一个灵活的粒子引擎, 你可以通过修改一些设置完成很多不同效果。 这些灵活的发射器可以很容易的放进已有的3D实时引擎中来增加一个仿真的真实性和趣味性。

  源代码和应用程序展示了粒子系统的使用。 发射器设置可以通过一个对话框定制效果。 这些设置可以被保存起来, 作为一个发射器库(包含很多发射器, 每种发射器是一种效果)。 要获取源代码和程序, 请到Game Developer的站点www.gdmag.com。

 

参考

Reeves, William T.Particle Systems. A Technique for Modeling a Class of Fuzzy Objects.ComputerGraphics, Vol. 17, No. 3 (1983): 359-376.

Reeves, William T.Approximate and Probabilistic Algorithms for Shading and Rendering StructuredParticles Systems.Computer Graphics, Vol. 19,

No. 3 (1985):313-322.

Watt, Alan, 3DComputer Graphics.

Reading, Mass.:Addison Wesley, 1993.


相关问题推荐

  • 回答 17

    还是要学好编程基础呀如果你觉得编程很苦难 不一定要从c#开始学  学学js flash as等等  有个梯度就好多了如果要用好unity  不会编程那是不行的  学习的过程中都有个头疼的过程  记住  头越痛  代表你要接受的东西越多  坚持 你的大脑在和知识兼容中:D...

  • unity如何自学Unity3D 2022-01-06 15:24
    回答 18

    可以先思考学习的目的,是什么因素在驱动你。是完成一款作品?进入某个行业?还是探究某类问题?否则和技术相关的知识浩如烟海,很容易迷失在细枝末节上。而要找到动力源头。个人的经验,就是关注一些和自己同方向,同类型的创作者。他们输出的作品会激励你,...

  • 回答 23

    可以让模型师直接作出这样的形状,如果用纯Unity制作,就要用基本游戏对象拼接了,包括楼梯,城堡,都可以拼接出来。正常情况不会这样做,因为不够精美,都是建模师来实现,毕竟Unity不属于专业的建模软件,侧重于实现功能。...

  • 回答 2

    Shader Unlit/Test{Properties{_MainTex(MainTex,2D)=white{}_MainColor(MainColor,COLOR)=(1,1,1,1)_AddTex(AddTex,2D)=white{}_Maxset(Max,Range(0.1,1.0))=0.1}SubShader{Tags{RenderType=Transparent Queue=Tran...

  • 回答 4

    文章主要为大家详细介绍了Unity Shader实现水波纹效果,文中示例代码介绍的非常详细具体代码实现如下:Shader Custom/shuibowen{ Properties{ _MainTex(Base (RGB),2D)=white{} _distanceFactor(Distancefactor,float)=1 _timeFactor(time factor,float)=...

  • 回答 7

    策划的最基本的原则就是:改进缺点,做别人没有做到的。无论游戏策划还是其它策划都是一样!    游戏策划的第二个原则:放飞思想。也许你认为我是说策划们应该充满想象力,能想一些匪夷所思的东西!对不起。不是这意思!一个合格的策划不是为了发泄自己的...

  • 回答 7
    已采纳

    可以多玩一些其它的游戏,看一些科幻电影等,寻找灵感。

  • 回答 3
    已采纳

    游戏架构与设计不纯粹是一门科学,它不需要提出假设或探究真理,也不被逻辑或正规方法的严格标准所束缚。游戏的目的就是通过玩来获得娱乐,因此游戏设计即需要艺术家一样的创造力,也需要工程师一样的精心规划。游戏设计是一门手艺,就像好莱坞的电影摄像或服...

  • 回答 5

    void Update(){          transform.rotation = Quaternion.Euler(Vector3.zero);}可以试一下,保证物体x轴和z轴为0就可以使其一直垂直。

  • 回答 3

    界面左右移动、上下移动。。本质都是:手指滑动。。。可以参考这些:https://www.cnblogs.com/coldcode/p/5362537.htmlhttps://blog.csdn.net/totosj/article/details/80112852https://blog.csdn.net/zcc858079762/article/details/85253120...

  • 回答 6

    首先新建一个C#脚本,命名为MyFollow,然后把以下代码粘贴进去,保存:AخA 1using UnityEngine;2using System.Collections;3public class MyFollow : MonoBehaviour4{5    public float distanceAway = 5;          // distance...

  • 回答 5

    安装高通的Vuforia插件即可。

  • 回答 5

    不可以,只能一个工程打一个包。

  • 回答 2

    Edit->Project Settings->Graphics 找到Shader Stripping 中fog mode设置为custom(原来是Automatic),然后选中你想要的模式,最后重新打包就ok

  • 回答 2

    用到的插件:System.Drawing.dllSystem.Windows.Forms.dllSystem.Deployment.dll(运用基于.Net4.x的dll打包时,需要用到该dll,否则会报错)代码如下:using System;using System.Runtime.InteropServices;using UnityEngine;using UnityEngine.UI; p......

  • 回答 6

    如下图,设置为none,然后删掉滑动条就可以了。

没有解决我的问题,去提问