快速模型高亮显示

提:
之前的shader无法满足在应用期间高亮显示(或其他shader特效),需要通过复制一份需要高亮的模型,为这部分模型添加高亮Shader或修改之前模型材质
    
    
     
     
1 . 问题 1 :增加模型面数浪费性能, 1 . 解决办法:用同一份模型,通过传参,改变Shader的渲染方式。 2 . 步骤: 1 . 开发Shader替换原先Shader,给美术同事。 2 . 通过代码,统一控制需要高亮的模型高亮 2 . 问题 2 :每次替换新模型的时候,都需要美术在开发的电脑上操作,浪费大量时间 1 . 解决办法:美术人员,只做一套模型,开发拿来就用 2 . 步骤: 1 . 技术美术把相关shader给美术,让美术直接替换
优化方向
    
    
     
     
- 通过一个脚本参数来控制其高亮与正常Shader的切换 - 美术提供的模型与材质与这个功能解耦:当需要替换模型改动材质时,对我实现这个功能不会增加工作量
假设需求
美术给我一个机器人,我要快速的将机器人的左右手脚依次点亮
如何做
  1. 找到单独存放手脚的 父物体 ,逐一遍历出来它下面的所有物体——遍历出需要添加材质的物体
  2. 为其(子物体及子物体的子物体)动态添加材质球-(发光材质球)。
  3. 将遍历与添加功能合并起来,
  4. 对添加功能的对象记录下来,便于将这些物体的发光材质关闭和再次添加
  5. 将核心代码封装成方法就可以随便用了
  6. 如用到Timeline里
实现时需要掌握的知识点
找到单独存放手脚的父物体,逐一遍历出来它下面的所有物体
使用 Transform.GetComponentsInChildren<>() 的方法来获取指定物体下的所有子、孙物体,并复制给数组,然后进行遍历
GetComponentsInChildren<>()
    
    
     
     
Transform [ ] father = GetComponentsInChildren < Transform > ( ) ; foreach ( var child in father ) { Debug . Log ( child . name ) ; } //transform.GetComponentsInChildren<Transform>(true); //获取全部子物体,无论SetActive是否为true // 想要父物体下所有有材质的对象 //MeshRenderer[] father = obj.GetComponentsInChildren<MeshRenderer>();
为其 物体 动态添加发光材质球。
GetComponent<MeshRenderer>().materials = myMaterial; //将赋值好的新材质或数组可以直接付给MRend

动态为MeshRenderer添加新材质

    
    
     
     
public Material [ ] mMaterial ; //创建一个材质数组 public MeshRenderer rend ; //创建有MRend实例 void Start ( ) { rend = GetComponent < MeshRenderer > ( ) ; //声明让MRend实例等于这个对象自身的MRend 为了获取其身上的数据 mMaterial = new Material [ rend . materials . Length + 1 ] ; //声明new材质数组等于原MRend的材质数量+1 +1是为了给高亮材质留着的 } void Update ( ) { if ( Input . GetKeyDown ( KeyCode . A ) ) //注意,这只是测试代码,多点A会超出索引 { for ( int i = 0 ; i < rend . materials . Length + 1 ; i ++ ) //在这里为new材质数组具体赋值 { if ( i < rend . materials . Length ) { mMaterial [ i ] = GetComponent < MeshRenderer > ( ) . materials [ i ] ; //让new材质没有超过原材质数量的数组等于原本的材质 } else mMaterial [ i ] = Resources . Load ( "FerBox23new" ) as Material ; //让超出原来数量的数组等于新的材质球 } this . GetComponent < MeshRenderer > ( ) . materials = mMaterial ; //将赋值好的材质数组直接付给MRend } Debug . Log ( rend . materials . Length . ToString ( ) ) ; }
也可以动态替换第一个材质,动态删除任何一个材质,动态改变任何材质的贴图或属性。
其他补充
    
    
     
     
//动态替换第一个材质 public Material mMaterial ; public MeshRenderer rend ; void Start ( ) { mMaterial = Resources . Load ( "FerBox23new" ) as Material ; //替换的材质存放的地址 rend = GetComponent < MeshRenderer > ( ) ; rend . material = mMaterial ; Debug . Log ( rend . materials . Length . ToString ( ) ) ; } //动态删除任何一个材质 Destroy ( this . GetComponent < MeshRenderer > ( ) . materials [ 1 ] ) ;
动态改变任何材质的贴图或属性。
将遍历与添加功能合并起来
将遍历与添加功能合并起来 过程
    
    
     
     
public GameObject obj ; void Update ( ) { if ( Input . GetKeyDown ( KeyCode . C ) ) { MeshRenderer [ ] father = obj . GetComponentsInChildren < MeshRenderer > ( ) ; foreach ( var child in father ) { Material [ ] mMaterial ; MeshRenderer rend ; rend = child . GetComponent < MeshRenderer > ( ) ; mMaterial = new Material [ rend . materials . Length + 1 ] ; for ( int i = 0 ; i < rend . materials . Length + 1 ; i ++ ) { if ( i < rend . materials . Length ) { mMaterial [ i ] = child . GetComponent < MeshRenderer > ( ) . materials [ i ] ; } else mMaterial [ i ] = Resources . Load ( "FerBox23new" ) as Material ; } child . GetComponent < MeshRenderer > ( ) . materials = mMaterial ; } } }
对添加功能的对象记录下来,便于将这些物体的发光材质关闭和再次添加
需增加一个脚本SonLightSwitch
GetObj脚本用于找到需要的物体,并为其初始化高亮功能 SonLightSwitch 负责记录本物体已被高亮功能初始化,和携带属性和方法方便GetObj调用
补充完整代码:加入添加与删除材质球功能,与防多填操作
GetObj
    
    
     
     
using UnityEngine ; /// <summary> /// 找物体并对物体进行操作 父 /// </summary> public class GetObj : MonoBehaviour { public GameObject obj ; void Update ( ) { if ( Input . GetKeyDown ( KeyCode . C ) ) { //限定Obj,将选择物体下的所有子孙物体中带MRend的存起来。 MeshRenderer [ ] father = obj . GetComponentsInChildren < MeshRenderer > ( ) ; foreach ( var child in father ) { //如果当前物体没添加脚本,添加材质球,后添加脚本。 if ( ! child . GetComponent < SonLightSwitch > ( ) ) { Material [ ] mMaterial ; MeshRenderer rend ; rend = child . GetComponent < MeshRenderer > ( ) ; mMaterial = new Material [ rend . materials . Length + 1 ] ; for ( int i = 0 ; i < rend . materials . Length + 1 ; i ++ ) { if ( i < rend . materials . Length ) { mMaterial [ i ] = child . GetComponent < MeshRenderer > ( ) . materials [ i ] ; } else mMaterial [ i ] = Resources . Load ( "FerBox23new" ) as Material ; } child . GetComponent < MeshRenderer > ( ) . materials = mMaterial ; child . gameObject . AddComponent < SonLightSwitch > ( ) ; child . GetComponent < SonLightSwitch > ( ) . isAddLight = true ; } //如果当前物体添加了脚本,并且新的发光材质球被销毁了,重新为材质位挂上新材质 else if ( child . GetComponent < SonLightSwitch > ( ) . isAddLight == false ) { child . GetComponent < SonLightSwitch > ( ) . AddLightMat ( ) ; } } } if ( Input . GetKeyDown ( KeyCode . V ) ) { MeshRenderer [ ] father = obj . GetComponentsInChildren < MeshRenderer > ( ) ; foreach ( var child in father ) { //对已经添加材质的物体,进行销毁发光材质球处理 if ( child . GetComponent < SonLightSwitch > ( ) ) { child . GetComponent < SonLightSwitch > ( ) . DestroyLightMat ( ) ; } } } } }
SonLightSwitch
    
    
     
     
using UnityEngine ; /// <summary> /// 从属,被GetObj挂在被选物体上,用于记录操作和装载方法 /// </summary> public class SonLightSwitch : MonoBehaviour { Material [ ] mMaterial ; public bool isAddLight ; public int materialsIndex = 0 ; /// <summary> /// 被GetObj 创建的脚本,当脚本被挂在物体上时候,物体已经被添加了新的发光材质 /// 这个脚本只需要控制所在物体的新增材质的开关就好 /// </summary> void Start ( ) { materialsIndex = GetComponent < MeshRenderer > ( ) . materials . Length ; mMaterial = new Material [ materialsIndex ] ; } //为删除的材质位置重新添加发光材质 public void AddLightMat ( ) { for ( int i = 0 ; i < materialsIndex ; i ++ ) { if ( i < materialsIndex - 1 ) { mMaterial [ i ] = GetComponent < MeshRenderer > ( ) . materials [ i ] ; } else mMaterial [ i ] = Resources . Load ( "FerBox23new" ) as Material ; } GetComponent < MeshRenderer > ( ) . materials = mMaterial ; isAddLight = true ; } //将新添加的发光材质删除 public void DestroyLightMat ( ) { Destroy ( this . GetComponent < MeshRenderer > ( ) . materials [ materialsIndex - 1 ] ) ; isAddLight = false ; } }
将核心代码封装成方法就可以随便用了
    
    
     
     
using UnityEngine ; /// <summary> /// 找物体并对物体进行操作 父 /// </summary> public class GetObj : MonoBehaviour { public GameObject obj ; void Update ( ) { if ( Input . GetKeyDown ( KeyCode . C ) ) { //限定Obj,将选择物体下的所有子孙物体中带MRend的存起来。 MeshRenderer [ ] father = obj . GetComponentsInChildren < MeshRenderer > ( ) ; foreach ( var child in father ) { ······ } } if ( Input . GetKeyDown ( KeyCode . V ) ) { ······· } } ························································ public void OpenObjs ( GameObject obj ) //新生成某一个父类下面的所有物体新材质 { MeshRenderer [ ] father = obj . GetComponentsInChildren < MeshRenderer > ( ) ; foreach ( var child in father ) { //如果当前物体没添加脚本,添加材质球,后添加脚本。 if ( ! child . GetComponent < SonLightSwitch > ( ) ) { Material [ ] mMaterial ; MeshRenderer rend ; rend = child . GetComponent < MeshRenderer > ( ) ; mMaterial = new Material [ rend . materials . Length + 1 ] ; for ( int i = 0 ; i < rend . materials . Length + 1 ; i ++ ) { if ( i < rend . materials . Length ) { mMaterial [ i ] = child . GetComponent < MeshRenderer > ( ) . materials [ i ] ; } else mMaterial [ i ] = Resources . Load ( "FerBox23new" ) as Material ; } child . GetComponent < MeshRenderer > ( ) . materials = mMaterial ; child . gameObject . AddComponent < SonLightSwitch > ( ) ; child . GetComponent < SonLightSwitch > ( ) . isAddLight = true ; } //如果当前物体添加了脚本,并且新的发光材质球被销毁了,重新为材质位挂上新材质 else if ( child . GetComponent < SonLightSwitch > ( ) . isAddLight == false ) { child . GetComponent < SonLightSwitch > ( ) . AddLightMat ( ) ; } } } public void DestroyObj ( GameObject obj ) //销毁这个物体下的新材质 { MeshRenderer [ ] father = obj . GetComponentsInChildren < MeshRenderer > ( ) ; foreach ( var child in father ) { //对已经添加材质的物体,进行销毁发光材质球处理 if ( child . GetComponent < SonLightSwitch > ( ) ) { child . GetComponent < SonLightSwitch > ( ) . DestroyLightMat ( ) ; } } } }
如用到Timeline里
Signal Track可以来完成
信号轨道,意思就是在特定时间想外部发出一些信号,让外部去处理它,是一个Timeline和外部联系的一个特别好的渠道
有这三部分组成,project里的信号资产,Timeline里的信号发射位置,Scene里的信号包裹
project里的信号资产 可以直接创建
Scene里的信号包裹
Signal 轨道上的物体身上需要挂载SignalReceiver组件的
Timeline里的信号发射位置
在轨道上添加信号的方法
而其他的轨道上也可以添加Signal信号帧来向外界发射事件,让外界对其作出反应
展示
还可以做什么
  1. 将核心代码与鼠标的射线规定,鼠标射线返回它指的物体地址。将地址传给核心代码(obj)。 让鼠标只哪 哪亮
  2. 示教类指示玩家的交互形式
待解决问题
目前当关闭高亮时候,是将添加的材质球销毁了,但MRend里还保留着这个位置。不透明材质看不出来什么问题,原本不透明材质当销毁高亮材质后,留下的MRend空位就会出现洋红色。
高亮闪烁Shader代码
    
    
     
     
Shader "Unlit/LightShader" { Properties { _MainTex ( "MainTex" , 2D ) = "white" { } _MainColor ( "MainColor" , Color ) = ( 1 , 1 , 1 , 1 ) _Emiss ( "Emiss" , Float ) = 1.0 _RimPower ( "RimPower" , Float ) = 1.0 _Lerp ( "Lerp" , Vector ) = ( 1 , 0 . 5 , 0 , 0 ) _TiemS ( "TiemS" , Float ) = 1 } SubShader { Tags { "Queue" = "Transparent" } Pass { ZWrite off Blend SrcAlpha One CGPROGRAM # pragma vertex vert # pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION ; float2 uv : TEXCOORD0 ; float3 normal : NORMAL ; } ; struct v2f { float4 pos : SV_POSITION ; float2 uv : TEXCOORD0 ; float3 normal_world : TEXCOORD1 ; float3 view_dir : TEXCOORD2 ; } ; sampler2D _MainTex ; float4 _MainColor ; float _Emiss ; float _RimPower ; float2 _Lerp ; float _TiemS ; v2f vert ( appdata v ) { v2f o ; o . pos = UnityObjectToClipPos ( v . vertex ) ; o . normal_world = normalize ( mul ( float4 ( v . normal , 0.0 ) , unity_WorldToObject ) . xyz ) ; float3 pos_world = mul ( unity_ObjectToWorld , v . vertex ) . xyz ; o . view_dir = normalize ( _WorldSpaceCameraPos . xyz - pos_world ) ; o . uv = v . uv ; return o ; } float4 frag ( v2f i ) : SV_Target { float3 normal = normalize ( i . normal_world ) ; float3 view_dir = normalize ( i . view_dir ) ; float NdotV = saturate ( dot ( normal , view_dir ) ) ; float3 col = _MainColor . xyz * _Emiss ; float fresnel = pow ( 1.0 - NdotV , _RimPower ) ; float mulTime20 = _Time . y * _TiemS ; float temp_output_1_0_g5 = mulTime20 ; float lerpResult22 = lerp ( _Lerp . x , _Lerp . y , ( ( abs ( ( ( temp_output_1_0_g5 - floor ( ( temp_output_1_0_g5 + 0.5 ) ) ) * 2 ) ) * 2 ) - 1.0 ) ) ; float alpha = saturate ( lerpResult22 * fresnel * _Emiss ) ; return float4 ( col , alpha ) ; } ENDCG } } }

猜你喜欢

转载自blog.csdn.net/unityofficial/article/details/132103132