Unity Shader:用几何着色器实现图元转换,细分,以及粒子系统
2018-02-11 22:17
791 查看
(上图:效果图1。普通网格模型)
(上图:效果图2。几何着色器粒子化特效进行中。)
(上图:效果图3。几何着色器粒子化特效进行中。)
1,用几何着色器进行图元转换
在OpenGL渲染管线中,几何着色器(Geometry Shader)有一个独一无二的工功能,既是图元转换。这里不对理论进行过多的讨论,可简单理解为渲染点,线,面之间的转换。实现很简单,只有注意语法即可,以本文中Shader为例:
-声明着色器:
#pragma geometry geom
-设置输出顶点数量:
[maxvertexcount(120)]
-声明输入与输出struct:
struct v2g { float4 vertex : SV_POSITION; fixed4 color:COLOR; float3 normal:NORMAL; }; struct g2f { float4 vertex : SV_POSITION; fixed4 color:COLOR; };
-设置几何着色器输入参数与输出值:“PointStream”决定了输出类型,“triangle v2g”必须与顶点着色器输出的图元类型一致,当输入为三角形图元时,结构数组长度必须为[3],既是每次同时被输入一个三角形的三个顶点。
void geom(inout PointStream<g2f> OutputStream,triangle v2g input[3]) { ... }
-将输出顶点传送至输出stream上:
OutputStream.Append(o);
其他图元之间的转换语法上大同小异。
2,用几何着色器对图元进行细分
除了细分着色器,几何着色器也可以用来进行细分工作。例如本例中将模型进行了粒子化,为了加强效果,增加粒子数量,对每个三角形图元都进行了细分。但是输出的顶点数是有上限的,根据输出stream的结构体(上面的g2f)的大小,Shader会对输出顶点数做出限制,本例中,每个三角形最多可以转换为120个粒子顶点,“[maxvertexcount(120)]”。
细分的算法:
-首先需要三个向量,一个位置向量作为起点,以及从起点至另两个顶点的方向向量:
V1 = (input[1].vertex - input[0].vertex).xyz; V2 = (input[2].vertex - input[0].vertex).xyz; V0 = input[0].vertex.xyz;
-接下来,利用V1,V2对三角形进行参数化。
(上图:参考自http://web.engr.oregonstate.edu/~mjb/cs519/Handouts/geometry_shaders.1pp.pdf)
这个算法可理解为,从V0出发,以V1为方向行进x个单位,以V2为方向行进y个单位,可到达三角形内任意一点:
(上图:从V0出发到任意点P0,P1)
int numLayers =1<<_Level; //2^_Level float dt = 1.0f / float( numLayers ); float t = 1.0f; for( int it = 0; it < numLayers; it++ ) { float smax = 1.0f - t; int nums = it + 1; float ds = smax / float( nums - 1 ); float s = 0; for( int is = 0; is < nums; is++ ) { float3 v = V0 + s*V1 + t*V2; ...... s += ds; } t -= dt; }
上面代码删去与细分无关的部分,核心思想既是在双重循环中等距的向V1,V2方向移动,对三角形进行细分。
(上图:用此算法对一个Quad进行细分,由于输出顶点数达到了极限(120),中间部分为空白。)
_Level函数可用来控制细分中t方向的密度:
(上图:_Level=4)
(上图:_Level=1)
3,用几何着色器构建粒子系统
此shader中Shader中的粒子特效有位移动画和淡出效果。-粒子位移
通过控制脚本,CPU不断更新Shader1的unityTime变量。Shader.SetGlobalFloat ("unityTime", Time.time);
在Shader内计算动画累计时间:
float time_SinceBirth=(unityTime-_ShaderStartTime)*0.1f;
计算重心坐标:
CG=(input[0].vertex.xyz + input[1].vertex.xyz+ input[2].vertex.xyz)/3.0f;
位移:根据动画时间进行位移加速。此行代码决定了此Shader中粒子的移动效果,如果想模拟真实物理效果可套入一些公式。此行代码没什么特殊思想,视觉上表现为粒子向某个方向进行直线指数级加速移动。
v = CG + vel*(_Speed*time_SinceBirth+1.0f) + 0.5f*_DispDir.xyz*sin(it*is)*(_Speed*time_SinceBirth)*(_Speed*time_SinceBirth);
-淡出效果
根据动画累计时间对alpha值进行递减让粒子逐渐消失:o.color=_FinalColor; o.color.w=1.0f-smoothstep(0,1.0f,time_SinceBirth);
除了通过alpha进行淡出处理也可以通过语义ponitsize对粒子size进行缩小处理以达到淡出(但目前在unity sahder中使用此语义无法通过编译)。
4,源码:
Shader:
Shader "Unlit/ParticleExp_Beta" { Properties { //细分相关变量 _Level("Level",int)=0 _DispDir("Displacement Direction",Vector)=(0,0,0) _uVelScale("VelScale",float)=2 //粒子化特效相关变量 _Speed("Speed",Range(0,1))=1 _ShaderStartTime("Shader Start Time",float)=0 _FinalColor("Final Color",color)=(1,1,1,1) } SubShader { Tags{"RenderType"="Transparent" "Queue" = "Transparent"} LOD 100 Pass { Blend SrcAlpha OneMinusSrcAlpha // use alpha blending cull off CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma geometry geom #include "UnityCG.cginc" //CPU输入变量 ////细分相关变量 uniform int _Level; uniform float3 _DispDir; uniform float _uVelScale; ////粒子化特效相关变量 uniform float _Speed; //粒子位移速度 uniform float _ShaderStartTime; //粒子化起始时间 uniform fixed4 _FinalColor; //粒子颜色 //内部变量 float3 V0, V1, V2; float3 CG; float unityTime; struct appdata { float4 vertex : POSITION; float3 normal:NORMAL; }; struct v2g { float4 vertex : SV_POSITION; fixed4 color:COLOR; float3 normal:NORMAL; }; struct g2f { float4 vertex : SV_POSITION; fixed4 color:COLOR; }; v2g vert (appdata v) { v2g o; o.vertex = v.vertex; o.normal=UnityObjectToWorldNormal(v.normal); return o; } [maxvertexcount(120)]//v2g input[3] void geom(inout PointStream<g2f> OutputStream,triangle v2g input[3]) { float time_SinceBirth=(unityTime-_ShaderStartTime)*0.1f; g2f o = (g2f)0; V1 = (input[1].vertex - input[0].vertex).xyz; V2 = (input[2].vertex - input[0].vertex).xyz; V0 = input[0].vertex.xyz; CG=(input[0].vertex.xyz + input[1].vertex.xyz+ input[2].vertex.xyz)/3.0f; int numLayers =1<<_Level; //2^_Level float dt = 1.0f / float( numLayers ); float t = 1.0f; for( int it = 0; it < numLayers; it++ ) { float smax = 1.0f - t; int nums = it + 1; float ds = smax / float( nums - 1 ); float s = 0; for( int is = 0; is < nums; is++ ) { float3 v = V0 + s*V1 + t*V2; float3 vel = _uVelScale * ( v - CG ); v = CG + vel*(_Speed*time_SinceBirth+1.0f) + 0.5f*_DispDir.xyz*sin(it*is)*(_Speed*time_SinceBirth)*(_Speed*time_SinceBirth); o.vertex = UnityObjectToClipPos(float4( v, 1.0f )); o.color=_FinalColor; o.color.w=1.0f-smoothstep(0,1.0f,time_SinceBirth); OutputStream.Append(o); s += ds; } t -= dt; } } fixed4 frag (g2f i) : SV_Target { return i.color; } ENDCG } } }
控制脚本:
挂在场景中任意物体上,用来接受输入以及向shader传入一些必要实时参数。using System.Collections; using System.Collections.Generic; using UnityEngine; public class ParticleExpController : MonoBehaviour { public Material particleExp; public MeshRenderer[] smRs; private Material[] originalMaterial; public GameObject model; // Use this for initialization void Start () { } IEnumerator EXP(){ smRs=model.GetComponentsInChildren<MeshRenderer>(); Material p_exp = new Material (particleExp); p_exp.SetFloat ("_ShaderStartTime", Time.time); for (int i = 0; i < smRs.Length; i++) { Material[] temp=smRs[i].materials; for(int j=0;j<smRs[i].materials.Length;j++){ temp [j] = p_exp; } smRs [i].materials = temp; yield return new WaitForSeconds (0.5f); } } // Update is called once per frame void Update () { if(Input.GetKeyDown(KeyCode.E)){ StartCoroutine (EXP ()); } Shader.SetGlobalFloat ("unityTime", Time.time); } }
参考:
OPENGL编程指南–Khronos Group
GLSL Geometry Shader–Mike Bailey–
(http://web.engr.oregonstate.edu/~mjb/cs519/Handouts/geometry_shaders.1pp.pdf)
射线和三角形的相交检测–DirectX
(http://www.cnblogs.com/graphics/archive/2010/08/09/1795348.html)
维护日志:
2018-2-24:填词改句
相关文章推荐
- Unity Shader:细分着色器(Tessellation Shader)在顶点与片段着色器中的写法以及各参数变量解释。
- unity 固定管线shader转换到顶点片段着色器
- unity总结--粒子系统的实现
- Unity Shader:实现菲涅尔+色散效果以及相关原理解析
- 用Shader实现的YUV到RGB转换:使用3重纹理实现
- @V@ java代码笔记2010-06-12:java控制台输入各类型类实现;以及判断输入字符串里面是否有数字的两种方法:方法1:转换成字符数组;方法2:正则表达式。
- Unity 5着色器系统代码介绍(上)
- 【干货】利用MVC5+EF6搭建博客系统(四)(上)前后台页面布局页面实现,介绍使用的UI框架以及JS组件
- 矩阵分解在推荐系统的应用以及python代码的实现
- unity shader 实现自由放大缩小效果
- Day36 JDBC 2 PrepareStatement和JDBC的封装以及MVC分层模式+员工管理系统的实现
- Modern OpenGL用Shader拾取VBO内单一图元的思路和实现
- unity shader 实现常见的混合效果
- 获取系统时间以及将时间戳转换成字符串,将字符串转换成时间戳
- 系统幂等以及常用实现方式
- [Shader 着色器]解读Unity中的CG编写Shader系列4——unity中的圆角矩形shader
- 粒子系统实现
- C#实现camel字符串转换(以及查阅后总结的一些其他C#中string类中的方法)
- Android中JSON数据解析:系统自带JSONObject以及第三方GSON的实现
- ios之让输入键盘隐藏消失的方法/以及系统通知的实现