(图1:正常渲染)

(图2:几何着色器粒子化特效进行中)

(图3:几何着色器粒子化特效进行中)

1,用几何着色器进行图元转换

在OpenGL渲染管线中,几何着色器Geometry Shader有一个独一无二的功能,既是图元转换。可简单理解为对基本图元点,线,三角形等等之间的转换。基本图元是由顶点组成的,所以几何着色器可以在函数内拿到一个基本图元的所有组成顶点,例如输入图元如果是三角形,它可以拿到三个顶点。

几何着色器相关语法很简单,以本文中Shader为例:

声明着色器:

#pragma geometry geom

设置输出顶点数量:

[maxvertexcount(120)]

声明输入与输出struct:

            struct v2g   //vertex to geometry{float4 vertex : SV_POSITION;fixed4 color:COLOR;float3 normal:NORMAL;};struct g2f   //geometry to fragment{float4 vertex : SV_POSITION;fixed4 color:COLOR;};

配置几何着色器的输入参数与输出值:“PointStream”决定了几何着色器的输出数据结构,“triangle v2g”必须与顶点着色器输出(既是几何着色器的输入)的图元类型一致,数组长度也要一致,例如当它为三角形图元时,结构数组长度必须为[3],既是每次同时被输入一个三角形的三个顶点。

           void geom(inout PointStream<g2f> OutputStream,triangle v2g input[3]){...}

在本案例中,一个三角形分解为3个点,需要一个方法将输出的点收集起来:

OutputStream.Append(o);

其他图元之间的转换在语法上大同小异。

2,用几何着色器对图元进行细分

除了细分着色器(见另一篇文章),几何着色器也可以用来进行细分工作。
例如本例中将模型网格进行了粒子化,为了加强效果,增加粒子数量,对每个三角形图元都进行了细分。几何着色器对图元进行细分后输出的顶点数是有上限的,根据输出stream的结构体(例如本例中的g2f)的大小,Shader会对输出顶点数做出限制,本例中,每个三角形最多可以转换为120个粒子顶点,“[maxvertexcount(120)]”。

细分的算法

首先需要三个向量,一个位置向量V0作为起点,以及从起点至另两个顶点的方向向量V1,V2:

           V1 = (input[1].vertex - input[0].vertex).xyz;V2 = (input[2].vertex - input[0].vertex).xyz;V0 = input[0].vertex.xyz;

接下来,利用V1,V2对三角形进行参数化。

(图4:三角形参数化。原图来自http://web.engr.oregonstate.edu/~mjb/cs519/Handouts/geometry_shaders.1pp.pdf)

我对三角形参数化的理解是:从V0出发,以V1为方向行进x个单位,再以V2为方向行进y个单位,可到达三角形内任意一点:

(图5:从V0出发按照V1,V2方向行进到达点P0,P1)

Shader代码实现

           int numLayers =1<<_Level;      //2^_Levelfloat 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方向移动,移动过程中找出的所有点既是三角形细分后的点的集合。

(图6:一个待细分的quad)


(图7:用此算法对quad进行细分后,由于输出顶点数达到了极限120,中间部分为空白)

_Level变量可用来控制细分中t方向的密度:

(图8:_Level=4)

(图9:_Level=1)

3,用几何着色器构建粒子系统

使用了几何着色器后,它就成为了面向顶点编程的最后阶段,可在此对顶点进行动画编程。例如案例中的Shader在几何着色器中实现了一个位移动画和淡出效果。

粒子位移

在顶点属性准备阶段,C#脚本不断更新一个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 5.x版本中使用此语义无法通过编译)。

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 100Pass{Blend SrcAlpha OneMinusSrcAlpha // use alpha blendingcull offCGPROGRAM#pragma vertex vert#pragma fragment frag#pragma geometry geom#include "UnityCG.cginc"细分相关变量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^_Levelfloat 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}}
}

C#动画触发控制脚本:

挂在场景中任意gameObject上。主要功能是收集model上的所有材质球并对其进行逐渐替换以实现逐渐粒子化的效果。另一个思路是同时替换所有材质球,然后根据uv或位置坐标进行更细腻的粒子化渐变。

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 initializationvoid 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 framevoid 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:填词改句
2018-6-10:改标题
2020-8-16:review

Unity Shader:用几何着色器实现复联3灭霸的终极大招灰飞烟灭相关推荐

  1. Unity Shader之几何着色器(Geometry Shader)实现面片飞散的爆炸效果

    前言 上篇文章初学集合着色器实现了草地效果,这篇再次使用GS实现一个"爆炸效果".为什么要加上引号呢,因为实现的其实不是一个传统的爆炸效果,更类似于面片向外扩散消失的一种科幻效果, ...

  2. Unity Shader:细分着色器(Tessellation Shader)在Unity顶点着色器中的写法以及各参数变量解释

    图1:在Unity内将sphere细分后 图2:在Unity内将sphere细分后 Unity官网关于细分着色器的资料比较少,只有在Surface Shader中使用的例子.我看了下Surface S ...

  3. OpenGL 几何着色器Geometry Shader

    OpenGL几何着色器Geometry Shader 几何着色器Geometry Shader简介 使用几何着色器 造几个房子 爆破物体 法向量可视化 几何着色器Geometry Shader简介 在 ...

  4. 第二十二章 opengl之高级OpenGL(几何着色器)

    OpenGL 使用几何着色器 用点造物体 爆破物体 法向量可视化 在顶点和片段着色器之间有一个可选的几何着色器(Geometry Shader), 几何着色器的输入是一个图元(如点或三角形)的一组顶点 ...

  5. 【OpenGL】笔记二十七、几何着色器

    1. 流程 在顶点和片段着色器之间有一个可选的几何着色器(Geometry Shader),几何着色器的输入是一个图元(如点或三角形)的一组顶点.几何着色器可以在顶点发送到下一着色器阶段之前对它们随意 ...

  6. UnityShader[4]几何着色器与可交互草地

    GeometryShader执行顺序在顶点着色器之后,片元着色器以前.GeometryShader以一个/多个顶点组成的图元为输入,开发人员可以修改/添加顶点,修改为完全不同的网格,得到更多好看的效果 ...

  7. LearnOpenGL学习笔记——几何着色器

    几何着色器 在顶点和片段着色器之间有一个可选的几何着色器(Geometry Shader),几何着色器的输入是一个图元(如点或三角形)的一组顶点.几何着色器可以在顶点发送到下一着色器阶段之前对它们随意 ...

  8. LearnOpenGL-高级OpenGL-9.几何着色器

    本人初学者,文中定有代码.术语等错误,欢迎指正 文章目录 几何着色器 使用几何着色器 造几个房子 爆破物体 法向量可视化 几何着色器 简介 在顶点和片段着色器之间有一个可选的几何着色器 几何着色器的输 ...

  9. Unity之几何着色器--草随风摇曳

    1. 简介12 1.1 主要工作: 通过HeightMap生成地形网格 通过分块的思想生成草的初始定位顶点 通过几何着色器配合伪随机数生成草的网格 通过伪随机函数来对风进行模拟 通过Blinn Pho ...

最新文章

  1. PyTorch | (4)神经网络模型搭建和参数优化
  2. BZOJ2132 圈地计划
  3. 搭建Keras,TensorFlow运行环境
  4. 传统企业如何实现移动化转型?
  5. php获取qzonetoken,QQ一键登录实现
  6. TCP重组数据包分析
  7. opencv计算图像亮度调节_OpenCV教程创建Trackbar图像对比度、亮度值调整
  8. maven 强制jdk的版本
  9. 《通信原理》复习笔记4----第四章信道相关例题
  10. 算法学习系列(MCMC):MCMC采样
  11. 计算机二级题库病毒,计算机二级MS Office题库
  12. swift 指定区域截图,自定义不规则图形截图
  13. (简单控制) 关于使用NI max 的GPIB来控制安捷伦万用表34401A 的操作方法
  14. 攻防世界逆向-logmein
  15. HDU 6405 Make ZYB Happy 后缀自动机 前缀和优化
  16. android 虚拟按键root,(免root)虚拟按键手动修改方法
  17. 存储器——嵌入式系统
  18. 演讲如何克服紧张情绪
  19. 2019年测试行业展望
  20. 如何用友远程到服务器,用友远程通安装常用的管理配置

热门文章

  1. Java求两个数的最大公约数
  2. phalapi做登录检测_phalApi
  3. 在matlab中function,Matlab中function函数使用操作方法
  4. 【项目调研+论文阅读】基于BERT的中文命名实体识别方法[J] | day6
  5. hibernate的查询条件lt_鱼与熊掌得兼:Hibernate与Mybatis共存
  6. 下滑加载更多js_vue.js怎么实现滑动到底部加载更多数据效果?
  7. java导出excel_java按需导出Excel并自动合同单元格
  8. html5星期,HTML5 时钟
  9. 事务回退机制 android,【Android基础】——Fragment-使用方法
  10. 红外倒车雷达原理图_自动驾驶汽车传感器技术解析—毫米波雷达