欢迎光临
我们一直在努力

实战:草地的建模&风动&交互

一、Blender —— 手搓一个简易“低模草丛”

我们要做的不是一根草,而是一坨草(Grass Clump)。

1、模型形体创建和调整

打开 Blender,删掉默认方块。

建面片:Shift + A -> Mesh -> Plane。

立起来:按 R (旋转) -> X (沿X轴) -> 90 (度)。

形状调整 (Edit Mode):

        进入 Tab 编辑模式。

        Ctrl + R (环切):横着切 2-3 刀(给它加点关节,不然弯的时候会折断)。

        把上面的点 S (缩放) 弄小一点,变成尖尖的形状。

组合成丛:

        在 Object Mode,Shift + D (复制) 几个面片。

        按 R -> Z,把它们转成“米”字形或者随机交叉。

细节调整:

        连通选择(L):在Edit Mode下,鼠标悬停在独立草叶上按L(Linked)快速选中整片叶子(避免只选到一个面)

        轴向锁定缩放:

                S + Z:单独调整高度(高矮变化)

                S + Shift + Z:锁定高度值调平水面(胖瘦/覆盖面积变化)

模型面数与覆盖率的权衡(Trade-off)。保留较宽的叶片以减少引擎内所需的Instance数量,优化Overdraw。

2、涂顶点色 (Vertex Color)

原理:利用顶点色(Vertex Color)作为数据载体传递给Shader,不作为颜色贴图。

  • 黑色(RGB 0, 0, 0) = 根部 = 顶点位移权重为0(固定不动)
  • 红色(RGB 1, 0, 0) = 梢 = 顶点位移权重为1(随风摆动)

操作:

合并物体(Ctrl + J):绘制前必须确保草丛是单一物体,否则无法统一绘制。

选中草,左上角模式选 Vertex Paint (顶点绘制)。

  • 初始化(Shift + K):在顶点绘制模式下使用Set Color Attribute将全模型填充为黑色(根部数据)
  • 绘制动态区:切换红色笔刷,手动涂抹草叶上半部分。

这样进 Unity 后,Shader 就能读到:红色=动,黑色=锁死。

导出:Export FBX (记得 Apply Transform,Z-forward, Y-up)。

效果图

遇到问题:导出后 Unity 里全是白色?

现象:在 Blender 里明明画好了红黑渐变,导入 Unity 后检查 Inspector,虽然有 Vertex Color 数据,但显示效果全是白的。 原因:Blender 的“颜色属性 (Color Attributes)”列表中存在多层数据。

画在了 Layer A 上,但 Blender 的“渲染相机图标”选中了空白的 Layer B。

导出 FBX 时,Blender 默认导出了选中的空白层。

解决:在 Blender 右下角属性面板,删除多余的颜色图层,只保留涂好颜色的那一层,确保导出无误。

导出的选项设置

导出保存到电脑中后,进入我们的unity。因为我一开始是Built-in管线,现在要改成URP,所以借这个机会进行一个Built-in转成URP的教学。

  • 在Project窗口(资源管理器)里,找个干净的文件夹
  • 右键空白处 -> Create -> Rendering -> URP Asset (with Universal Render)。注意不同版本名字可能微调,找带“URP Asset”字样的就行
  • 会得到两个文件,比如叫New URP Asset和New Asset_Render。建议重命名方便查找,这里我就命名为了URP_Config。
  • 现在要激活URP,这个过程中,如果你一开始是Built-in,那场景中可能会出现大量“粉色”,这是正常的因为老材质不认识新管线。所以继续接着打开菜单栏:Edit -> Project Settings
  • 左侧点击Graphics
  • 最上面有一个Scriptable Render Pipeline Settings(SRP设置),现在应该是空的(None)
  • 把刚才创建的URP_Config(那个Asset文件)拖进去,或者点小圆圈选中它。
  • Unity自带修复粉色材质的功能。接着打开菜单栏:Window -> Rendering  -> Render Pipeline Converter(旧版本可能是Edit -> Render Pipeline -> Upgrade Project Materials…,但是建议用Converter)
  • 点击左下角的Initialize Converters(初始化转换器),它会扫描项目里所有能转的材质
  • 扫描完后,点击右下角的Convert Assets。
  • 读条结束后,场景里90%的粉色材质应该都会变回正常颜色

    常见问题 (Q&A)
    Q1: 转换完还是有东西是粉红色的?

    A: 说明那个材质用了自定义 Shader (不是 Unity 自带的 Standard)。

    解决:这种无法自动转换。你需要手动把它的 Shader 更换为 Universal Render Pipeline/Lit,或者用 Shader Graph 重写它。

    Q2: UI 消失了或者不显示?

    A: 检查 Canvas (画布) 的 Render Mode。如果是 Screen Space – Camera,记得把 Main Camera 重新拖进 Render Camera 槽位里。

    Q3: 项目升级到URP后,运行游戏,WASD按键失效,人物呆立不动?

    A: 检查 Input System。Project Settings -> Player -> Active Input Handling -> 改为 Both。

    原因:URP项目模板默认启用了新版输入系统(Input System Package),而老项目的角色控制器脚本通常使用的是旧版Input Manager。


    二、Shader Graph  —— 交互核心(风动)

    我使用的是顶点位移(Vertex Displacement)技术,即不改变物体Transform,直接在GPU层面修改顶点位置。

    新建一个 URP Unlit Shader Graph,命名为SG_Grass。

    1. 连线逻辑详解

            第一步:制造动力 (The Motor)

                    节点:Time -> Sine

                    原理:Time 提供持续增长的时间值,Sine 将其转化为 -1 到 1 的往复波动值(模拟左右摇摆)。

            第二步:控制幅度与方向 (Amplitude & Direction)

                    节点:Multiply (A) -> Vector 3

            连线:     

                    Sine 输出 -> Multiply A (乘以 0.002)。(将波幅限制在微风级别)

                    Multiply A 输出 -> Vector 3 的 X 输入口。(指定草只在 X 轴方向摇摆)

            第三步:应用遮罩 (The Mask) 

                    节点:Vertex Color -> Split -> Multiply (B)

            原理:

                    使用 Split 节点把顶点颜色的 R 通道 拆分出来。

                    根部 (黑, R=0):风力 × 0 = 0 (不动)。

                    叶尖 (红, R=1):风力 × 1 = 全风力 (晃动)。

                    将 Vector 3 的结果与 Split R 相乘。

            第四步:位移应用 (The Application)

                    节点:Position -> Add -> Master Vertex

                    原理:新位置 = 原始位置 + 风的偏移量。


    遇到问题:连线后草“消失”了?

            现象:只要把逻辑连到 Vertex Position,场景里的草就不见了。

            原因:Position 节点的坐标空间设置错误。

    我之前选了 World (世界空间)。

    Shader 的顶点着色器默认是在 Object (物体空间) 运作的。

    后果:Unity 再次对世界坐标进行了转换,导致坐标数值爆炸(飞出天外)。 

    解决:将 Position 节点的下拉菜单改为 Object。

    2.最终美化:染色 (Lerp)

    草动起来了,但还是红黑色的“数据模样”。我们需要把它变成绿色。

    节点:Lerp (插值)

    原理:

            A (起点):设置为深绿色 (模拟根部/阴影)。

            B (终点):设置为嫩绿色 (模拟叶尖/受光)。

            T (控制):连接 Vertex Color 的 R 通道。

    效果:R=0 显示深绿,R=1 显示嫩绿,中间自动过渡。

    补充一个顶点位移公式:FinalPos = OriginalPos + Offset * Mask

    我图片中深绿嫩绿的颜色对比没有特别明显,我觉得有一点点过渡就好了,自然一点。

    Shader Graph连线图如下

    效果图


    三、C# 传导数据 (The Bridge)

    现在要做的叫做“交互式草地”(Interactive Grass)。

    原理:Shader计算草的位置和玩家位置的距离,如果距离近,就把草往反方向“推”一把。

    这需要C#(告诉Shader玩家在哪)和Shader Graph(计算推力)配合完成。

    1、C#脚本

    我们需要一个脚本挂在Player上。

    using UnityEngine;

    [ExecuteInEditMode] //加上这个就可以在不运行游戏时也能看到草倒伏,方便调试
    public class GrassInteraction : MonoBehaviour
    {
    void Update()
    {
    //把玩家当前的位置,发送给所有的Shader
    //“_PlayerPos”是我们在Shader Graph里要用的暗号
    Shader.SetGlobalVector("_PlayerPosition", transform.position);
    }
    }

    2、Shader Graph:

    • 计算玩家与草的距离 -> 决定推力大小(越近推力越大)
    • 计算玩家指向草的方向 -> 决定倒伏方向
    • 将计算出的推力向量叠加到之前的风力偏移上

    我们需要在原有的风动逻辑之外新增一个“推力计算模块”

    1. 定义全局变量

            在 Shader Graph 的 Blackboard 中新建 Vector3 属性,命名为 PlayerPosition。

            关键设置:Reference 必须改为 _PlayerPosition(与 C# 对应)。

    2. 计算推力方向 (Direction)

            公式:Dir = Normalize(GrassPos – PlayerPos)

            节点:

                    Position (World) 减去 _PlayerPosition。

                    连接 Normalize 归一化。

                    踩坑点:此时的方向是 3D 的。如果玩家比草高,草会被“按进地里”。

                    修正:使用 Split + Vector3 将 Y 轴强制设为 0,只保留水平推力。

    3. 计算推力强度 (Strength)

            公式:Strength = 1 – Distance(GrassPos, PlayerPos)

            节点:

                    Distance 节点计算距离。

                    One Minus 节点反转数值(距离越近,数值越大)。

                    Saturate 节点限制范围在 0-1 之间。

    4. 融合与应用

            合成推力:将 方向 乘以 强度,再乘以 根部遮罩(Vertex Color R)(防止根部飞起)。

            强度微调:最后乘以一个 Float (0.02),避免推力过大导致模型拉伸成“长矛”。

            最终输出:将结果通过 Add 节点,叠加到原本的风力计算结果上,最后连接至 Vertex Position。

    遇到问题:草变成了“长矛”

    现象:人物一靠近草,草瞬间伸长几米远

    原因:Shader中的位移是基于Object Space的,而场景中的草对象被放大了10倍。Shader里0.3的位移在屏幕上变成了3.0。

    解决:在Shader中将推力强度系数从0.3调小至0.02(可以自己调试)


    因为这次的Shader Graph线有点多,有点绕,所以想做一个更好理解的补充。

    把我们的草想象成一个“提线木偶”。

     故事一:木偶的“自动舞蹈” (风动)

    这是让草自己摇摆的部分。

    Time (时间):这是电池。没有它,木偶就是死的。

    Sine (正弦波):这是节拍器。它把直来直去的时间,变成了“左-右-左-右”的节奏。

    Multiply (乘法):这是音量旋钮。

            原来的节奏幅度太大(甩头 1 米远)。

            乘个小数字(0.002),把它调成“微风”。数字可以自己调试

    Vector 3 (组合):这是方向盘。告诉木偶:“你只能横着摇 (X轴),别上下跳”。

    总结:这一块就是给木偶装了个自动马达。

     故事二:木偶的“条件反射” (交互)

    这是最绕的部分(涉及世界坐标),我们把它想象成磁铁。

    Position (World) & PlayerPosition:这是GPS 定位。

            草手里有个 GPS,玩家手里有个 GPS。

    Distance (距离):这是测距仪。

            测算两个 GPS 离得有多近。

    One Minus (一减):这是反转逻辑。

            原本是:离得越远,距离数字越大。

            我们要:离得越近,推力越大。所以用 1 减去距离。

    Subtract (减法):这是指南针。

    草的位置 – 玩家位置 = 逃跑的方向。

    (就像磁铁同极相斥,玩家过来了,草要往反方向躲。)

    总结:这一块是给木偶装了个雷达,看见玩家来了就躲。

     故事三:木偶的“钉子” (遮罩)

    Vertex Color (顶点色):这是钉子。

    黑色 (根部):被钉在地板上,怎么推都动不了 (× 0)。

    红色 (梢部):是自由的,随风动,随人动 (× 1)。

    总结:如果没有这个,草就会整棵飞起来。

    赞(0)
    未经允许不得转载:171主机测评 » 实战:草地的建模&风动&交互
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址