【Unity】广告牌(Billboard)材质
2016-04-27 20:11
796 查看
转载网址:http://www.myexception.cn/mobile/1981419.html
Billboard概述
Billboard技术在游戏引擎中占有很重要的地位,一般用在粒子效果或者光晕效果上,让粒子面片z轴朝向摄影机。Unity里面也不例外,在Unity的粒子系统中的render面板的render mode中就四中billboard模式可选,这些模式都是面对摄影机的,只是轴向的约束不同;另外Unity的Terrain系统里也有Billboard技术的使用;但是如果我们使用自己做的模型或者预设,要使它朝向摄影机的话,则得自己写脚本或者写相应的shader实现,在这里将拿shadowgun里面的一个写得不错的shader(MADFINGER/Transparent/Blinking GodRays Billboarded)做例子实现billboard功能(如下图:),原来的例子中有一些与billboard无关的效果(如闪烁,根据距离渐隐等)我将它去掉了,只讲述billboard的实现。
模型在3d软件中的准备
在这个shader里面,为了获得面片的中心,将一些数据存在顶点颜色和UV2里面,在顶点颜色R通道和G通道里面存储一个单位面片(长宽为1的面片)的坐标,即四个顶点的顶点色(0,0,0),(255,0,0),(255,255,0),(0,255,0),而在UV2里面存下这个面片的长宽值(6,8),需要注意的是要保证单位为米,方便匹配unity的单位;如下图本例使用的面片:四个顶点的顶点色如下图所示,长宽为6和8,四个顶点的UV2如下图所示,记录长宽的单位6和8。shader实现
这个shader是一个可以约束Y轴的billboard效果,因此在属性里我们需要定义一个参数,来控制轴向约束的开关:_VerticalBillboarding("Vertical Restraints", Range(0,1)) = 1
接下来主要工作是在vert函数里修改面片模型的顶点,进行顶点位置的计算,首先我们先获得面片中心到摄影机位置的向量;这个时候我们存在模型顶点颜色和uv2的数据就可以起作用了,下面的计算都是在object
space下计算的。
float3 centerOffs = float3(float(0.5).xx - v.color.rg,0) * v.texcoord1.xyy;//利用事先存在模型顶点色和UV2的数据获得每个顶点相对与中心的偏移量 // float3 centerOffs = float3(float(0.5).xx - v.color.rg,0) * v.color.bbb*255; float3 centerLocal = v.vertex.xyz + centerOffs.xyz;//将顶点坐标偏移到中心位置 float3 viewerLocal = mul(_World2Object,float4(_WorldSpaceCameraPos,1));//获得观察点在object space下的坐标 float3 localDir = viewerLocal - centerLocal;//获得面片中心到观察点的方向向量 localDir.y =localDir.y * _VerticalBillboarding;//如果_VerticalBillboarding为0,则面片则绕y轴旋转
然后将得到的方向向量正则化,并将该方向向量作为Z轴,重构出面片中心面对视角的右手方向向量rightLocal和向上方向向量upLocal;
float3 rightLocal; float3 upLocal; CalcOrthonormalBasis(normalize(localDir) ,rightLocal,upLocal);//localDir的正则化很重要!
这里使用了CalcOrthonormalBasis(float3 dir,out float3 right,out float3 up),该函数作用就是根据dir重构出右手方向和向上方向up;这个函数是需要自己定义的 ,原型如下:
void CalcOrthonormalBasis(float3 dir,out float3 right,out float3 up) { up = abs(dir.y) > 0.999f ? float3(0,0,1) : float3(0,1,0); right = normalize(cross(up,dir)); up = cross(dir,right); }
从上述原型可以看出dir,right,up是相互垂直的向量,从而构建出一个在坐标系统;在这里我们的输入时归一化后的localDir向量,因此得到一个在object space下的以localDir作为Z轴,rightLocal做X轴,upLocal做Y轴的新的坐标系,而localDir视线方向,所以rightLocal和upLocal
所构成的平面与视线垂直;最后我们根据这两个单位向量将之前偏移到面片中心的顶点坐标centerLocal重新按偏移量centerOffs偏移回去,构成一个垂直于视线的新的平面;
float3 BBLocalPos = centerLocal - (rightLocal * centerOffs.x + upLocal * centerOffs.y);
当然这个新的平面是在object space下的,还需要转换到屏幕投影空间;
o.pos = mul(UNITY_MATRIX_MVP, float4(BBLocalPos,1));//w分量设为1表明float4(BBLocalPos,1)是一个顶点
VF版本代码01
Shader "PengLu/Billboard/UnlitAddVF" { Properties { _MainTex ("Base texture", 2D) = "white" {} _VerticalBillboarding("Vertical Restraints", Range(0,1)) = 1 } SubShader { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } Blend One One Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) } LOD 100 CGINCLUDE #include "UnityCG.cginc" sampler2D _MainTex; float _VerticalBillboarding; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; }; void CalcOrthonormalBasis(float3 dir,out float3 right,out float3 up) { up = abs(dir.y) > 0.999f ? float3(0,0,1) : float3(0,1,0); right = normalize(cross(up,dir)); up = cross(dir,right); } v2f vert (appdata_full v) { v2f o; float3 centerOffs = float3(float(0.5).xx - v.color.rg,0) * v.texcoord1.xyy; //float3 centerOffs = float3(float(0.5).xx - v.color.rg,0) * v.color.bbb*256; float3 centerLocal = v.vertex.xyz + centerOffs.xyz; float3 viewerLocal = mul(_World2Object,float4(_WorldSpaceCameraPos,1)); float3 localDir = viewerLocal - centerLocal; localDir.y =localDir.y * _VerticalBillboarding; float3 rightLocal; float3 upLocal; CalcOrthonormalBasis(normalize(localDir) ,rightLocal,upLocal); float3 BBNormal = rightLocal * v.normal.x + upLocal * v.normal.y; float3 BBLocalPos = centerLocal - (rightLocal * centerOffs.x + upLocal * centerOffs.y); o.uv = v.texcoord.xy; o.pos = mul(UNITY_MATRIX_MVP, float4(BBLocalPos,1)); return o; } ENDCG Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest fixed4 frag (v2f i) : COLOR { return tex2D (_MainTex, i.uv.xy); } ENDCG } } }
VF版本代码01效果:
相关文章推荐
- [置顶] Unity与Android交互-android的安装与配置
- 【Unity】使用C#中的委托类型实现代理设计模式
- untiy 3d ShaderLab_第 2 章Unity中Shader(着色器)的形态_3_Shader的数据接口:属性和 uniform变量
- untiy 3d ShaderLab_第 2 章Unity中Shader(着色器)的形态_2_Unity中Shader的3种形态
- untiy 3d ShaderLab_第 2 章Unity中Shader(着色器)的形态_1_Unity通过ShaderLab 来组织Shader
- Unity打包同一文件Hash不一样
- Shader之学习笔记一
- unity c#和c++调用
- 【Unity】基于MVC模式的双肩包系统 UGUI实现
- Unity3d开发(十二)使用Menu.SetCheck更改菜单勾选状态
- Unity3D 自定义事件(事件侦听与事件触发)
- Unity之Quaternion学习三
- Unity3D DFGUI根据名称获取多个子控件代码
- Unity3d 播放高质量视频解决方案
- 【小松教你手游开发】【unity实用技能】foreach为什么在unity不建议用
- 【小松教你手游开发】【unity实用技能】NGUI Depth探索
- 【小松教你手游开发】【系统模块开发】射线触发按钮
- 【小松教你手游开发】【系统模块开发】动态可拖动列表DynmicList,ScrollView
- 【小松教你手游开发】【unity实用技能】从NGUI的UIScrollview的实现原理延伸到ngui的层次,合并,drawcall生成原理
- 【小松教你手游开发】【unity实用技能】u3d 层次问题总结