关于URP中RendererFeature的使用及毛发效果的几种实现方式

目录

前言

一、铺垫

二、基于程序网格复制的实现

三、基于程序多材质的实现

四、基于RenderObjects手动配置的实现

4.1、多Pass实现多层毛发

4.2、override material实现多层毛发

五、基于RendererFeature代码编写的实现

5.1、创建RendererFeature模板

5.2、先熟悉一下各个方法

5.3、在RenderObjects源码中找它对材质做了什么

5.4、实现RenderObjects的参数面板

5.5、CommandBuffer实现多次绘制

六、资源链接


前言

——发这篇文主要还是想记录一下最近针对URP中RendererFeature的初步学习。因为网上搜的各种资料个人感觉对我这种水平的都太不友好了- -所以也算是写一篇自己看得懂的教程来给自己看,相信也适合刚玩U3D shader没多久的萌新_(:з)∠)_

——RendererFeature这玩意也算是个人小两年前刚接触shader时的一个历史遗留问题了,当时因为对U3D和程序的理解都不够所以搜了些资料也看不懂就搁置了···因为最近遇到一个需要做毛发效果的带蒙皮动画的模型,用之前那种网格复制的方式乍得一想解决不了动画的问题(其实传个权重就能简单解决- -···一会讲),所以下决心攻坚一波。

——经过国内外查了一大堆文章和视频资料,同时自己又去硬着头皮啃了啃U3D内置功能的源码试了又试,现在初级的RendererFeature的代码编写已经能实现了(放在最后讲)。但对其中很多像是CommandBuffer之类的功能模块的理解还很浅显,所以也希望专业的带佬能在评论区分享一下干货_(:з)∠)_

 ——然后说一下项目环境和版本。示例用的是unity 2020.3.36f1c1,URP 10.9(URP版本可在项目内上方window->package manage里看)。因为URP的频繁更新,所以一些内置功能和函数不太稳定,(比如renderfeature里要用到的ScriptableRenderPass类的OnCameraSetup方法在URP11里就变成了Configure)所以文章最后放的资源如果有报错可能是因为版本问题。

一、铺垫

——像上图这种密度较高但又需要做体积的效果,如果用的是U3D的内置管线而非URP,那么一种常见的套路是在SubShader里写多个pass。(shader没啥技术含量就不贴了- -网上类似的更好的一大堆,也可以直接到后面的资源里去找本文用的)

——每个pass的代码几乎完全相同,主要不同在于在顶点阶段需要根据当前层数来计算法线方向的顶点偏移量,这样便形成了同一个模型渲染了多层的效果。当层与层之间的间隔足够小时,离远了看上去就会像是一个有体积的突起(微积分的思想···另一种常见的描边效果也可以这么做,只不过只外扩了一层并剔除了正面)。但也因此,层数低或离得近时就会出现上图右侧明显的分层现象。

——问题在于,URP的shader本身不支持十几甚至几十个这么多pass的写法,即便写了实际也不会运行。如果像是描边这种两个pass就能搞定的效果还可以通过下面这样的方法来解决。

//第一个Pass
Pass
{
    Tags
    { "LightMode" = "SRPDefaultUnlit" }
    //其他代码
}

//第二个Pass
Pass
{
    Tags
    { "LightMode" = "UniversalForward" }
    //其他代码
}

//第三个Pass
Pass
{
    Tags
    { "LightMode" = "LightweightForward" }
    //其他代码
}

//SRPDefaultUnlit等是U3D内置的一些关键字。

——但草地和毛皮这种动不动十几二十层的就没辙了(因为内置关键字就三四个)。网上有些大佬说改管线源码自己加关键字可以让它识别,我只能说对于我这种水平,改管线什么的还是太远- -

——URP提供给我们实现多pass的方法就是RendererFeature(实际上它能做的远不止这个···)。

二、基于程序网格复制的实现

——本节的方法也是我以前不会用RendererFeature时做一些像是苔藓草坪之类的效果所使用的方案。

——简单来说,需要写一个C#脚本,在其中利用Mesh类提供的一些方法,基于原本的网格模型生成法向扩张的多层网格。其中像是网格膨胀的算法逻辑与shader中相同,关键代码如下。

float deltaThick = thick / (stepMax - 1);//单步厚度变化量
float deltaVCol = 1.0f / (stepMax - 1);//单步顶点色变化量,用来标识层数高低

//输出网格数据准备
Mesh mesh = new Mesh();
List<Vector3> verts = new List<Vector3>();
List<Color> colors = new List<Color>(); //顶点色记录层数归一化高度
List<Vector2> uvs = new List<Vector2>();
List<Vector3> normals = new List<Vector3>();
List<Vector4> tangents = new List<Vector4>();
List<int> indexs = new List<int>();
List<BoneWeight> weights = new List<BoneWeight>();

//输入的数据集
Vector3[] vertsIN = baseMesh.vertices;
Vector2[] uvsIN = baseMesh.uv;
Vector3[] normalsIN = baseMesh.normals;
Vector4[] tangentsIN = baseMesh.tangents;
int[] indexsIN = baseMesh.GetIndices(0);
BoneWeight[] weightsIN = baseMesh.boneWeights;

//遍历,顶点沿法线方向偏移
for (int stepIdx = 0; stepIdx < stepMax; stepIdx++)
{
    //数据
    for (int vIdx = 0; vIdx < baseMesh.vertexCount; vIdx++)
    {
        verts.Add(vertsIN[vIdx] + deltaThick * stepIdx * normalsIN[vIdx]);
        colors.Add(Color.white * deltaVCol * stepIdx);
        uvs.Add(uvsIN[vIdx]);
        normals.Add(normalsIN[vIdx]);
        tangents.Add(tangentsIN[vIdx]);
        if (weightsIN.Length > 0)
        {
            weights.Add(weightsIN[vIdx]);
        }
    }

    //顶点索引
    for (int i = 0; i < indexsIN.Length; i++)
    {
        indexs.Add(indexsIN[i] + baseMesh.vertexCount * stepIdx);
    }
}

//数据导入
mesh.SetVertices(verts);
mesh.SetColors(colors);
mesh.SetUVs(0, uvs);
mesh.SetNormals(normals);
mesh.SetTangents(tangents);
mesh.SetIndices(indexs, baseMesh.GetTopology(0), 0);
if (weightsIN.Length > 0)
{
    mesh.bounds = baseMesh.bounds;
    mesh.boneWeights = weights.ToArray();
    mesh.bindposes = baseMesh.bindposes;
}

——其中用顶点色记录了一下各层的高度,以便在shader中做AO,高度透明度阈值等相关计算。

——关于动画兼容的问题,其实只要加个骨骼权重数组weights,然后在结尾数据导入时把骨骼蒙皮相关的那些参数传递一下就可以保证复制出来的网格也是带蒙皮权重的了。

——但这方法有个问题。当原本的模型面数较高时,层数又设置的太高就会产生下面这样的崩坏。

 ——而低面数模型则无事发生。

 ——看上去感觉是顶点索引乱了,感觉算法逻辑也没问题- -又可能是和顶点权重有关?以前因为几乎都是在特效片上做的这个效果,所以没遇到过。如果有知道为啥的带佬希望能交流一下_(:з)∠)_

——下面是该方案的效果。

 

三、基于程序多材质的实现

——本节的方法是在之前研究RenderFeature时的副产物。以前没试过U3D里的多材质功能,也不知道它有啥样的效果。但在尝试下一节要讲的RenderObjects实现方法时,因为用到了复数个材质,我就联想到了Renderer组件自带的多材质功能。结果一试···

  • 13
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值