当前位置: 首页 > news >正文

佳木斯网站建设哪家好网络推广员

佳木斯网站建设哪家好,网络推广员,怎么做福彩网站,南京网站建设案例一、materials 与 sharedMaterials 1.1 使用上的区别与差异 Unity 开发时,在 C# 中通过 Renderer 取材质操作是非常常见的操作,Renderer 有两种常规获取材质的方式: sharedMaterials:可以理解这个就是原始材质,所有使…

一、materials 与 sharedMaterials

1.1 使用上的区别与差异

Unity 开发时,在 C# 中通过 Renderer 取材质操作是非常常见的操作,Renderer 有两种常规获取材质的方式:

  1. sharedMaterials:可以理解这个就是原始材质,所有使用了同一个材质资源的模型 renderer, sharedMaterial 相同,修改了 sharedMaterials 相当于就是修改了资源

  2. materials:material 这个相当于 material Instance,比如同一个箱子模型实例化两个 renderer, sharedMaterial 相同,这时候你想让其中一个箱子是红色的,另一个箱子是绿色的,这时候就可以使用 material,clone 不同的材质实例来做表现的差异化

当然,除了 clone material instance 来做表现差异化,Unity 更希望你用 MaterialPropertyBlock,这个才是做材质表现差异化的正确路子

先不提 MaterialPropertyBlock,我们拉回到 sharedMaterial,material,有带 s 的和不带 s 的,这个对应美术同学制作过程中的多维子材质的概念:也就是一个完整几何体是可以有多个材质,虽然渲染次数还是多次,但完整的几何体天然的解决缝合问题,并且如果相邻渲染,几何体可以不用重复传入,性能好一些,所以不管 sharedMaterials 还是 materials,都是数组的形式存储,对于没有 s 的,其实就是取数组的第一个元素,因为大多数情况数组里还是仅仅有一个元素

综上所述,我们脑海中的 Renderer 内部是什么样子的,一定有个 sharedMaterial Array 和 material Array,当 material Array 存在的时候,就用其来渲染,否则用 sharedMaterial Array 来渲染,这样我使用了 material,后边的逻辑想舍弃它继续用 sharedMaterial,只需置空 material Array 即可,真实的 Unity 是这样的规则吗?直接上 Unity 源码

1.2 Unity 源码分析

[NativeHeader("Runtime/Graphics/Renderer.h")]
public partial class Renderer : Component
{[FreeFunction(Name = "RendererScripting::GetMaterial", HasExplicitThis = true)] extern private Material GetMaterial();[FreeFunction(Name = "RendererScripting::GetSharedMaterial", HasExplicitThis = true)] extern private Material GetSharedMaterial();[FreeFunction(Name = "RendererScripting::SetMaterial", HasExplicitThis = true)] extern private void SetMaterial(Material m);[FreeFunction(Name = "RendererScripting::GetMaterialArray", HasExplicitThis = true)] extern private Material[] GetMaterialArray();[FreeFunction(Name = "RendererScripting::GetMaterialArray", HasExplicitThis = true)] extern private void CopyMaterialArray([Out] Material[] m);[FreeFunction(Name = "RendererScripting::GetSharedMaterialArray", HasExplicitThis = true)] extern private void CopySharedMaterialArray([Out] Material[] m);[FreeFunction(Name = "RendererScripting::SetMaterialArray", HasExplicitThis = true)] extern private void SetMaterialArray([NotNull] Material[] m);public Material[] materials{get{#if UNITY_EDITORif (IsPersistent()){Debug.LogError("Not allowed to access Renderer.materials on prefab object. Use Renderer.sharedMaterials instead", this);return null;}#endifreturn GetMaterialArray();}set { SetMaterialArray(value); }}public Material material{get{#if UNITY_EDITORif (IsPersistent()){Debug.LogError("Not allowed to access Renderer.material on prefab object. Use Renderer.sharedMaterial instead", this);return null;}#endifreturn GetMaterial();}set { SetMaterial(value); }}public Material sharedMaterial { get { return GetSharedMaterial(); } set { SetMaterial(value); } }public Material[] sharedMaterials { get { return GetSharedMaterialArray(); } set { SetMaterialArray(value); } }}
namespace RendererScripting
{Material* GetMaterial(Renderer* r);Material* GetSharedMaterial(Renderer* r);void      SetMaterial(Renderer* r, Material* m);dynamic_array<Material*> GetMaterialArray(Renderer* r);void GetMaterialArray(Renderer* r, dynamic_array<Material*>& mat);void GetSharedMaterialArray(Renderer* r, dynamic_array<PPtr<Material> >& mat);void SetMaterialArray(Renderer* r, const dynamic_array<Material*>& ma);}
 Material* RendererScripting::GetMaterial(Renderer* r)
{
#if UNITY_EDITORDebugAssert(!r->IsPersistent());
#endifreturn r->GetAndAssignInstantiatedMaterial(0, false);
}Material* RendererScripting::GetSharedMaterial(Renderer* r)
{return r->GetMaterialCount() ? r->GetMaterial(0) : 0;
}void RendererScripting::SetMaterial(Renderer* r, Material* m)
{r->SetMaterialCount(std::max(1, r->GetMaterialCount()));r->SetMaterial(m, 0);
}void RendererScripting::GetMaterialArray(Renderer* r, dynamic_array<Material*>& mat)
{
#if UNITY_EDITORDebugAssert(!r->IsPersistent());
#endifDebugAssert(r->GetMaterialCount() <= mat.size());for (int i = 0, in = r->GetMaterialCount(); i < in; ++i)mat[i] = r->GetAndAssignInstantiatedMaterial(i, false);
}void RendererScripting::GetSharedMaterialArray(Renderer* r, dynamic_array<PPtr<Material> >& mat)
{DebugAssert(r->GetMaterialCount() <= mat.size());for (int i = 0, in = r->GetMaterialCount(); i < in; ++i)mat[i] = r->GetMaterialArray()[i];
}dynamic_array<Material*> RendererScripting::GetMaterialArray(Renderer* r)
{dynamic_array<Material*> ret(r->GetMaterialCount(), kMemDynamicArray);RendererScripting::GetMaterialArray(r, ret);return ret;
}void RendererScripting::SetMaterialArray(Renderer* r, const dynamic_array<Material*>& ma)
{r->SetMaterialCount(ma.size());for (int i = 0, in = ma.size(); i < in; ++i)r->SetMaterial(ma[i], i);
}

上述代码仅仅截取了 Renderer 中 sharedMaterial 和 material 的源代码调用部分,其他部分暂时省去,先从源代码的 get 部分分析,可以发现:

  • material 的 get 最终调用来自于 Renderer::GetAndAssignInstantiatedMaterial 函数

  • sharedMaterial 的 get 最终调用来自于 Renderer::GetMaterial 函数

virtual PPtr<Material> Renderer::GetMaterial(int i) const override 
{ return m_Materials[i]; 
}
void Renderer::SetMaterial(PPtr<Material> material, int index)
{Assert(index < (int)m_Materials.size());m_Materials[index] = material;/*#if !DEPLOY_OPTIMIZEDMaterial* materialPtr = material;if (materialPtr && materialPtr->GetOwner ().GetInstanceID () != 0 && materialPtr->GetOwner() != PPtr<Object> (this)){ErrorString("Assigning an instantiated material is not a good idea. Since the material is owned by another game object, it will be destroyed when the game object is destroyed.\nYou probably want to explicitly instantiate the material.");}#endif*/SetDirty();
}
Material* Renderer::GetAndAssignInstantiatedMaterial(int i, bool allowFromEditMode)
{// Grab shared materialMaterial* material = NULL;if (GetMaterialCount() > i)material = GetMaterial(i);// instantiate material if necessaryMaterial* instantiated = &Material::GetInstantiatedMaterial(material, *this, allowFromEditMode);// Assign materialif (material != instantiated){SetMaterialCount(std::max(GetMaterialCount(), i + 1));SetMaterial(instantiated, i);}return instantiated;
}

从 Renderer 的源码分析,内部仅仅有一个 material 数组,而不是两个,这个和我们上述脑海中浮现的数据结构已经不一样了,很有意思,倒是要看看他到底怎么做的:因此到这继续深度分析,抛开诸多假象来看实际的本质

class EXPORT_COREMODULE Renderer : public Unity::Component, public BaseRenderer
{ ...........typedef dynamic_array<PPtr<Material> > MaterialArray;MaterialArray       m_Materials;    ///< List of materials to use when rendering.........
}

看上边 Renderer::GetAndAssignInstantiatedMaterial 的实现,发现其主要调用了 Material::GetInstantiatedMaterial 来实现的材质克隆,再粘一下代码

Material& Material::GetInstantiatedMaterial(Material* material, Object& renderer, bool allowInEditMode)
{if (material == NULL)material = GetDefaultMaterial();if (material->m_Owner == PPtr<Object>(&renderer))return *material;else{if (!allowInEditMode && !IsWorldPlaying())ErrorStringObject("Instantiating material due to calling renderer.material during edit mode. This will leak materials into the scene. You most likely want to use renderer.sharedMaterial instead.", &renderer);// Make sure the properties are initialized before we're cloning, otherwise we'll end up using the properties of the default materialmaterial->EnsurePropertiesExist();Material* instance;instance = CreateObjectFromCode<Material>();instance->SetNameCpp(Append(material->GetName(), " (Instance)"));instance->m_Shader = material->m_Shader;instance->m_Owner = &renderer;// Creating the material above already creates the shared material data, so release and create a new one using copy constructor.// Would be nice to avoid this extra work (but then, the default "create material" already does a bunch of extra other work// that we are discarding here; an optimization for some future day).SAFE_RELEASE(instance->m_SharedMaterialData);instance->m_SharedMaterialData = UNITY_NEW(SharedMaterialData, kMemMaterial)(*material->m_SharedMaterialData);instance->m_SharedMaterialData->smallMaterialIndex = instance->GetInstanceID();instance->CopySettingsFromOther(*material);instance->m_SavedProperties = material->m_SavedProperties;return *instance;}
}

从当中可以看出:

  1. Material 的 m_Owner 是否指向是 Renderer,决定此 Material 是否是 Renderer 的实例化材质(InstantiatedMaterial)
  2. 如果传入的 material 是①所述的实例化材质直接返回,否则就克隆一份 material 并都将归属 m_Owner 指向 renderer
  3. 克隆出来的实例材质名字规则是在原有 material 的名字后边追加字符串(Instance)

1.3 得出结论

  1. Renderer 仅有一份 material 数组,模型初始化后,其内容就为 sharedMaterial(以 mesh 类为例,可以理解 MeshRenderer 创建后,material 数组中的内容就是 prefab 上的材质资源列表
  2. 执行 renderer.material,无论左值还是右值都会触发 Renderer 的创建材质实例的函数,此时如果 material 数组中的材质已经是本 renderer 创造的材质实例,则直接返回,否则创建返回,它会覆盖 shareMaterial
  3. 在步骤②之后,如果再次调用 renderer.shareMaterial,右值则直接返回当前 material 数组中的材质(当然它已经不再是最早的那个 material 资产了,也就是说此 renderer.shareMaterial 非比原 renderer.shareMaterial),左值则会再次实例化创建新的 material 并赋值,material 数组中的材质会再次被覆盖

因此,不存在最早我们分析的:sharedMaterial 和 material 是泾渭分明的两套数组存储,他们最终 cache 在同一个数组里,就是说你调用了 renderer.material 后,renderer.sharedMaterial 也就变成了你最新克隆的 renderer.material,如果你需要找回初始的 renderer.sharedMaterial,就只能自己提前 cache

源码中 m_Owner 就是材质克隆归属的依据:如果不满足克隆规则,Unity 会重新克隆,这样很多时候都会和我们预想的事与愿违

觉得绕可以直接看下面的例子:

Renderer r = go.GetComponent<Renderer>();
Material ma = r.material;  
r.sharedMaterial = ms1;
Material mb = r.material; 

上述代码,假设 r.sharedMaterial 为 ms。

  • 执行完第2句,r.material 和 r.sharedMaterial 皆为 ms(Instance),ms 在此处的引用已经丢失
  • 执行完第3句,r.sharedMaterial 为 ms1
  • 执行完第4句,r.material 和 r.sharedMaterial 皆为 ms1(Instance),ms1 在此处的引用已经丢失

所以,替换 sharedMaterial 要谨慎,如果你直接修改了它(r.sharedMaterial),可能就会动到资源文件,但如果你在实例化了 material 之后再访问 renderer.shareMaterial,难以避免的会再次实例化一个新的材质,很明显这个时候就会出现资源的浪费(一个 GO 实例化了两个 material),因此尽量还是采用 MaterialPropertyBlock 来做材质个性化的事

二、MaterialPropertyBlock

https://www.jianshu.com/p/eff18c57fa42

使用MaterialPropertyBlock来替换Material属性操作 - UWA问答 | 博客 | 游戏及VR应用性能优化记录分享 | 侑虎科技

接上文,其实从应用层考虑的话,其实我们只是想实现一个简单的需求:那就是修改当前 GameObject(Renderer) 的材质属性

前面提到过,如果你通过 renderer.material 修改材质属性,那么其实底层相当于是给你实例化了一个新的 material,并且这个 material 专属于当前的 GO,其实这样也没有问题,只要你能管理好这个实例化后的 material 也不是不行

当然还有一个更快更省的方法,就是使用 MaterialPropertyBlock

//一个使用 MaterialPropertyBlock 及 Renderer.SetPropertyBlock 修改材质的例子
private void onFxCircleLoaded(GameObject obj, int fxId, object userData)
{if (!obj || userData == null){return;}Vector4 vec = (Vector4)userData;var mr = obj.GetComponentInChildren<MeshRenderer>();if (mr){var mpb = MCommonObjectPool<MaterialPropertyBlock>.Get();mr.GetPropertyBlock(mpb);mpb.SetVector("_Params", vec);mr.SetPropertyBlock(mpb);mpb.Clear();MCommonObjectPool<MaterialPropertyBlock>.Release(mpb);}
}

网上很多文章都会将 MaterialPropertyBlock 和 GPU Instancing 绑定讲解,但其实 MaterialPropertyBlock 本质上只是一种优化的手段:其还可以被用于 Graphics.DrawMesh 和 Renderer.SetPropertyBlock 两个 API,当我们想要绘制许多相同材质但不同属性的对象时都可以使用它(无论是否 GPU Instancing)

它和直接赋值 renderer.material 不同,完全不会产生额外的材质实例,使用 MaterialPropertyBlock 会直接覆盖某个渲染器上对应的属性,开辟一片新的存储空间存储当前变量而并非在原先的 cbuffer (此 cbuffer 非比 DX 里的 constant buffer,更准确的说法应该是指 Unity 材质属性区)里面,后面也不再从 cbuffer 中拿数据了,也因此它会打破 SRP Batcher

2.1 使用 MaterialPropertyBlock 的注意事项

  1. 没有必要在 shader 属性前面声明 [PerRendererData] 前缀:有教程将 [PerRendererData] 和 MaterialPropertyBlock 捆绑在了一起,其实它们没有直接的逻辑关系,[PerRendererData] 只影响 Editor 的显示行为,即当你通过 MaterialPropertyBlock 改变了某个 Material 的属性之后,只有加上了这个,才能在预览对应的 Material 面板上看到对应属性值的变更,很明显,这没有太大的意义
  2. MaterialPropertyBlock 会使得 SRP Batcher 不生效,这个上面刚提到过,毕竟这两种方法本质思路都是开辟一段新的内存用于数据的读取,很明显,在数据唯一的这一铁定条件下,它不可能存在于两块空间中,因此这两套方案可以说是平行/不相容

http://www.mnyf.cn/news/42754.html

相关文章:

  • 下了网站建设成都网络营销推广
  • 开源门户网站建设方案宁波网站制作优化服务公司
  • 前端外包网seo排名软件价格
  • 建设网站要求今日热点
  • 二维码转链接合肥seo推广排名
  • 盐城企业做网站多少钱人民日报评网络暴力
  • 腾讯会议收费seo是什么岗位的缩写
  • 加强志鉴网站建设南宁seo外包靠谱吗
  • 东营做网站优化优化网络的软件下载
  • 制作网站需要什么知识谷歌seo怎么优化
  • java php 网站建设网站建设优化
  • 礼泉做网站二十条优化疫情措施
  • 会网站制作的职业是市场调研报告word模板
  • 做鸡人网站seo服务外包
  • 国外网站要备案吗seo关键字优化技巧
  • 网站建设网站建活动营销
  • wordpress 去除tag吉林seo基础知识
  • 品牌网站建设怎么做seo优化排名怎么做
  • asp做微网站投稿平台
  • html5经管网站模板免费推广产品的网站
  • 胜芳哪里做网站中国刚刚发生的新闻
  • 福州做网站的公司厦门排名推广
  • 电子商务网站建设教程平台搭建
  • 福建建设执业中心网站推广员网站
  • 网站开发视频播放好做吗网站策划报告
  • 网站开发企业培训企点qq
  • 青岛代理公司注册新乡seo外包
  • 做按摩网站有生意吗西安快速排名优化
  • 龙华做棋牌网站建设哪家便宜郑州网站推广
  • 如何解决网站只收录首页的一些办法刚刚中国突然宣布