Shader笔记——使用粒子序列图实现体积云
最近看到一个效果还不错的体积云,了解了下其实现原理,将其实现思路做一个简单的整理和记录,并重写该Shader
其效果图:
该效果来自名为CloudPuff的Unity资源包中示例场景截图
实现的主要思路为:
使用MatCap对预先存储的光照进行采样
MatCap是使用存储了视空间下不同法线方向的球形光照纹理,在计算光照时直接对纹理进行采样,运算效率高,但也有其局限,只能适用于单一材质的模型,并且无法对光源位置和相机位置的变化做出响应。MatCap Shader的基本思路是,使用某特定材质球的贴图,作为当前材质的视图空间环境贴图(view-space environment map),来实现具有均匀表面着色的反射材质物体的显示。考虑到物体的所有法线的投影的范围在x(-1,1),y(-1,1),构成了一个圆形,所以MatCap 贴图中存储光照信息的区域是一个圆形。
关于MatCap的详细原理,可以参考这里和这里使用粒子的序列图播放实现云层的变化
勾选粒子的 TextureSheetAnimation 的选项,Shader中传入云层的序列帧纹理并设置相关参数即可实现云层的序列帧动画
Shader部分
Shder主要分为两部分,一个是边缘光Pass,另一个是主体光照颜色计算Pass:
边缘光Pass部分:
边缘光Pass部分比较简单,主要是对 边缘光的纹理进行采样:
v2f vert(a2v v){v2f o;o.pos=UnityObjectToClipPos(v.vertex);o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);o.scrPos=ComputeScreenPos(o.pos);o.color=v.color;return o;}fixed4 frag (v2f i):SV_Target{fixed4 col=tex2D(_MainTex,i.uv);col.rgb*=0;col.a*=i.color.a;fixed4 edgeCol=tex2D(_EdgeLight,i.scrPos.xy/i.scrPos.w);col.rgb=(edgeCol)*col.a*_EdgeStrength;return col;}
由于边缘光的纹理是通过RenderTexture对当前相机角度下的"场景"进行捕捉,采样时使用当前像素点对应的视口坐标,在计算颜色输出时,使用到了主纹理的Alpha通道值
主体光照计算部分
主体光照计算部分主要是对MatCap的球形纹理进行采样,由于纹理的坐标范围是[0,1],而视空间下的法线范围是在[-1,1],因此需要将视空间下的法线做一个范围映射:
即:
o.cap.xyz=worldNormal*0.5+0.5;
另外,在开启软粒子效果时,为了使粒子与场景中的有深度值的物体之间过渡自然,会有一个深度判断,并将判断结果影响其Alpha值,
雾效的叠加过程中,与边缘光的纹理处理类似,是通过RenderTexture对当前相机角度下的"场景"进行捕捉,采样使用当前像素点对应的视口坐标
着色器代码,附相关注释:
v2f vert(a2v v){v2f o;o.pos=UnityObjectToClipPos(v.vertex);o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);//法线变换,转置逆矩阵fixed3 worldNormal=normalize(unity_WorldToObject[0].xyz*v.normal.x+unity_WorldToObject[1].xyz*v.normal.y+unity_WorldToObject[2].xyz*v.normal.z);//转换法线到视空间worldNormal=mul((fixed3x3)UNITY_MATRIX_V,worldNormal);o.cap.xyz=worldNormal*0.5+0.5;//计算顶点在屏幕空间的位置,未归一化o.scrPos=ComputeScreenPos(o.pos);//如果使用软粒子效果,计算视空间下的深度值,后续与场景深度值作比较#ifdef SOFTPARTICLES_ONCOMPUTE_EYEDEPTH(o.scrPos.z);#endifUNITY_TRANSFER_FOG(o,o.pos);o.color=v.color;return o;}fixed4 frag(v2f i):SV_Target{//如果使用软粒子效果,通过深度值比较,距离云层近的物体,云层透明度高#ifdef SOFTPARTICLES_ON//对相机深度纹理采样(输入的是未归一化的srcPos,方法内部做srcPos.xy/srcPos.w透视除法,得到视口坐标)//通过LinearEyeDepth方法转换到视空间下的深度fixed sceneZ=LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture,UNITY_PROJ_COORD(i.scrPos)));//这里的i.scrPos.z经过 COMPUTE_EYEDEPTH(o.scrPos.z) 已经存储的是视空间里的深度fixed partZ=i.scrPos.z; fixed fade=saturate(_ParticleFade*(sceneZ-partZ));i.color.a*=fade;#endif//光照纹理采样fixed4 mc=tex2D(_MatCapLight,i.cap);mc.a=1;//主纹理采样fixed4 col=tex2D(_MainTex,i.uv);col.rgb*=i.color*mc*3;col.a*=i.color.a;//雾效叠加#if ADVFOG_ONfixed4 advFog=tex2D(_AdvFog,i.scrPos.xy/i.scrPos.w);col.rgb=col.rgb+(advFog*_FogStrength);#endif#if ADVFOG_ONadvFog.rgb*=0.75;UNITY_APPLY_FOG_COLOR(i.fogCoord,col,advFog);#endif#if ADVFOG_OFFUNITY_APPLY_FOG_COLOR(i.fogCoord,col,UNITY_LIGHTMODEL_AMBIENT);#endifreturn col;}
场景设置部分
场景设置中,需要提前准备与SkyBox对应的光照纹理,边缘光纹理,环境纹理,即:
![](/assets/blank.gif)
![](/assets/blank.gif)
![](/assets/blank.gif)
并将该纹理赋予三个双面球体,为什么要这么做呢?
前面提到过,MatCap的局限在于,使用MatCap的光照效果,由于纹理贴图是静态的,因此在场景中无法对光源和相机的位置变化做出光影反应,同理使用RenderTexture作为边缘光和雾效纹理也会有同样的问题
那如果这个纹理是动态的,会随着相机角度变化而变化呢?
因此,在上述 双面球体的中心分别使用三个相机,并与场景主相机保持同步旋转,这样采到的RendTexture就能随着主相机的角度变化而变化,由于光照纹理是使用MatCap,因此需要将相机改为正交模式,并将近裁剪面设置为 1,远裁剪面设置为-1,这样相机就能够得到一个球外视角,中间球形的动态纹理,
对于边缘光纹理和雾效纹理,相机保持透视模式,设置较小的远近裁剪面范围,只捕捉到双面球体内部的贴图就可以了
Shader的_MainTex为一个云朵的序列帧图:
新建一个粒子系统,并将该Shader生成的材质赋予该粒子系统,勾选 TextureSheetAnimation 选项,设置相关参数
单个粒子系统生成效果:
多个粒子系统生成效果:
Shader笔记——使用粒子序列图实现体积云相关推荐
- OpenGL shader笔记
OpenGL shader笔记 目录 OpenGL shader笔记 uniform varying 访问顶点 访问顶点--颜色 访问顶点属性--法线 gl_Normal 访问顶点--纹理坐标 at ...
- Unity学习shader笔记[一百]简单焦散Caustic效果
焦散是模仿光透过水底的一个投影景象 有两个版本,改版最后效果如下 这里是简单的基于物体的焦散,基于水体的焦散思路是水面物体的shader中拿到ColorBuffer,然后用水体的屏幕空间坐标取采集Co ...
- three.js学习笔记(七)——粒子
粒子可以被用来创建星星.烟雾.雨滴.灰尘.火焰等等.我们可以使用合理的帧速率来创建数千个粒子.每个粒子都是由始终面向摄影机的平面(俩个三角形)组成的. 创建粒子 创建粒子和创建网格很像,不同的是粒子使 ...
- simulink笔记——DSSS直接序列扩频
用simulink搭建DSSS链路,分别仿真在高斯信道和瑞利衰落信道下的误码性能,画出误码率曲线.瑞利衰落信道要使用引导(pilot)辅助. 模块: 参数说明:01出现可能性相等:每帧10个点. 功能 ...
- 【笔记】三张图读懂机器学习:基本概念、五大流派与九种常见算法
文章目录 [笔记]三张图读懂机器学习:基本概念.五大流派与九种常见算法 Chapter 1: A look at Machine learning 1.What is it? 2.How does m ...
- 【UE4】【笔记】5、UE4学习笔记 LOD与合并静态网格体
UE4官方视频学习笔记--LOD与合并静态网格体 大纲如下: UE4LOD自动创建工具 LOD自动创建工具 选择一个静态网格体,打开静态网格编辑器,并在细节面板中找到LOD Settings分组 ...
- Building Worlds In Unreal 学习笔记——03-06 地形贴图/地形材质/修正重复/近景位移
Building Worlds In Unreal 学习笔记--03-06 地形贴图/地形材质/修正重复/近景位移 Lec03 地形贴图 Landscape Textures 1 Quixel Bri ...
- Tensorflow2学习笔记:简单灰度图分类
Tensorflow2学习笔记:简单灰度图分类 相关介绍 实验环境 实验步骤 导入相关库 导入数据集 浏览数据 预处理数据 构建模型 设置层 编译模型 训练模型 向模型馈送数据 评估准确率 进行预测 ...
- 什么是序列图/时序图?
前言 UML Sequence Diagrams是交互图,详细说明了如何执行操作. 它们捕获协作环境中对象之间的交互. 序列图是时间焦点,它们通过使用图表的垂直轴来直观地显示交互的顺序,以表示消息的发 ...
最新文章
- 扫描全能王文件上传不了服务器,扫描全能王如何备份JPG 文件备份JPG办法
- RedisLive监控工具 windows部署笔记
- 关于建立 Carbon Forum 后 设置访问链接的问题
- java正则 链接_Java使用正则表达式匹配获取链接地址的方法示例
- python安装及配置
- 循环数组最大子段和--51nod1050
- TypeScript基础 ts文件的创建与执行
- 递归概述与递归能解决的问题和规则 [数据结构][Java]
- 企业测试环境部署(一)
- 机器学习【吴恩达|周志华|李宏毅|算法】清单
- MYSQL查询员工信息练习
- 5-TAMRA标记LYS赖氨酸,5-TAMRA-Lysine结构式及光谱图分享
- 图片合成视频,视频文件过大了,python 改变视频的码率, 压缩视频文件
- 与君共品代码: Spelling Corrector
- 车道级导航风口已至,谁在抢占前装车载市场新商机?
- oracle网页怎么翻译成中文,sql语句翻译成中文小工具
- matlab图像归一化方法
- FPGA给DSP6678复位
- 树莓派文件服务器nas,如何使用树莓派DIY一个NAS存储服务器
- SumperMap基本编程