顶点程序经典案例——树木生长
树木生长Shader
一、介绍
大家好,我是阿赵。这次来做一个树木生长的Shader。
顶点程序作为整个渲染管线里面和片段程序并列的两大可控过程之一,一直存在感都比较低。我们平时制作的效果,很多都是在片段程序里面实现的计算,顶点程序一般只是用来计算一下坐标系转换。
这次介绍的树木生长shader,我个人感觉是顶点程序的一个比较经典的应用,通过控制模型的顶点,做出一棵树的生长动画,还是比较有意思的。
另外一个知识点,就是黑白遮罩的运用。在深入学习各种效果的Shader编写之后,会发现很多效果的计算基础,都是模型不同部位的黑白颜色分布的计算。比如说透明度的计算,黑透白不透,比如边缘光的计算,边缘计算出白色,内部计算出黑色,等。
这个例子里面,黑白色控制了顶点是否显示,控制了树木生长的边缘,控制了刚长出来的部分的颜色,诸如此类。如果明白了黑白关系,在编写Shader的道路上,可以说是跨进了一大步。
其实我知道我在一开始写这么多废话,一般也不会有人看的。所以还是快点说制作过程吧。完整Shader在最后面。
二、制作过程说明
1、准备工作:
看到上面的视频,可能有些朋友会说,是不是直接在3DsMax里面做一段动画,然后直接在Unity里面播放呢?
其实不是的。我这里只在3DsMax里面准备了一棵树的模型,它包括了树干和树叶2个部分。

把模型导出fbx然后导入Unity后,把贴图附上,会看到这个模型的效果如下,并没有任何的动画。

地面是为了好看我随便找了一张贴图赋予了一个地面,这个不重要,我们只要关系树干和树叶就行了。
2、展UV
先说一下,为什么树木能实现生长的效果。
为了让树木能从根部到顶部,再到树叶逐渐的出现,我必须找到一个信息,是从树木根部一直到叶子渐变的。为了看得比较清晰,我先把叶子隐藏了,单纯用树干来做说明。

这种信息可以有很多,举个例子:
1.顶点颜色
2.UV坐标
3.贴图颜色
4.顶点坐标
等
通过这些手段,理论上都能实现到这种渐变的效果。但顶点坐标只能是从上到下,或者从左到右之类线性的控制,这里的树枝有可能是横向纵向甚至是拐弯的,所以并不适合用顶点坐标来做。所以我们可以在顶点颜色、UV坐标、贴图颜色这些数据里面选择一种比较容易实现的来做,都无所谓。
我这里选择的是UV坐标。
由于模型已经有了UV1信息用于漫反射贴图的UV坐标计算了。所以我要展一个UV2。

展完之后的UV2大概是这个样子的,我使用了uv的u坐标,从左往右在0-1的区间渐变。
把展好UV后的模型导入到Unity,然后单独显示一下我们需要的坐标看看。
3、检查UV信息显示
这里插一个知识点,介绍一下怎么单独去看模型的某个数据。
比如我现在要看UV2的信息,我可以在顶点程序里面先读取了uv2信息传入到片段程序,然后在最后的输出时
return half4(i.uv2.xxx,1);
这样,就可以单独把我们需要的信息当做颜色显示出来。
我这里的UV2的u坐标信息,显示完是这样的:

可以看到,现在树干从根部到树枝,是有一个由黑到白的渐变。
如果我想把颜色反过来,根部是白色,树枝是黑色,可以这样:
float val = 1- i.uv2.x
return half4(val.xxx,1);
这样,就能得到了之前显示的那个渐变效果了:

4、控制黑白渐变的过程
上面的黑白渐变图,发现有一个问题,就是黑白渐变的区域太大了,我们想做树木生长,一般是需要有一个比较明显的黑白分割线过渡的。
为了实现过渡范围的控制,可以使用smoothstep方法来控制:
float v2Val = 1 - v.uv2.x;
float heightVal = saturate(v2Val + _height);
float growVal = smoothstep(_min, _max, heightVal);
还是那个规则,黑的地方不显示,白的地方显示,所以就可以得到下面这个效果,可以看看黑白范围和最后显示效果的对比:




5、根据法线方向做树枝大小缩放
树木的生长并不是圆柱形生长出来,而是生长出来的地方是尖的,长出一段距离之后才慢慢变粗。
这个效果实现起来不难,还是刚才的黑白图过渡部分,我们可以使用黑白关系,然后加上模型顶点法线的方向,来做一个顶点沿着法线方向的缩放。
由于模型显示的过渡范围和变尖的过渡范围可能不一致,所以虽然都是用smoothstep来控制范围,但min和max的值可以不一样。
float v2Val = 1 - v.uv2.x;
float heightVal = saturate(v2Val + _height);
float growVal = smoothstep(_min, _max, heightVal);
heightVal = smoothstep(_endMin, _endMax, heightVal);


6、生长颜色的变化
最后,我还想实现一种效果,在树木刚长出来的时候,颜色是比较浅的,然后到了生长完成的时候,颜色会变深:


为了实现这种效果,我继续利用了刚才的黑白渐变过渡的信息,给他叠加一个颜色:
half3 diffuseCol = (1 - i.growVal)*_growCol + col.rgb;
这样在过渡的部分,就会乘以一个growCol,在生长完成之后,就纯是漫反射贴图的颜色。
7、树叶部分
其实刚才我们已经把整个shader做完了。树叶的部分,实际上不需要额外写shader去实现,因为展UV的时候,树叶也是根据出现的先后顺序,展UV在树枝的后面的。

这里我比较的偷懒,把相同形状的树叶的UV展在了一起了,所以实际出现的效果,就是同样形状的树叶会一起长出来。如果想效果更真实有点,可以根据整棵树的树枝生长先后顺序,来排列这些树叶的UV2.


三、完整shader
Shader "azhao/TreeGrow"
{Properties{_MainTex("Texture", 2D) = "white" {}_AlphaMap("AlphaMap", 2D) = "white" {}_height("height", Range(-1 , 1)) = 0_min("min", Range(0 , 1)) = 0_max("max", Range(0 , 1)) = 1_endMin("endMin", Range(0 , 1)) = 0_endMax("endMax", Range(0 , 1)) = 1_growCol("growCol", Color) = (0,1,0,0) }SubShader{cull offTags { "RenderType"="Opaque" }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;float2 uv2 : TEXCOORD1;float3 normal:NORMAL;};struct v2f{float4 vertex : SV_POSITION;float2 uv : TEXCOORD0;float2 uv2 : TEXCOORD1;float growVal : TEXCOORD2;};uniform float _min;uniform float _max;uniform float _height;uniform float _endMin;uniform float _endMax;uniform sampler2D _MainTex;uniform float4 _MainTex_ST;uniform float4 _growCol;uniform sampler2D _AlphaMap;v2f vert (appdata v){v2f o; o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.uv2 = v.uv2;float v2Val = 1 - v.uv2.x;float heightVal = saturate(v2Val + _height);float growVal = smoothstep(_min, _max, heightVal);heightVal = smoothstep(_endMin, _endMax, heightVal);heightVal = max(heightVal, growVal);float3 offsetVal = v.normal*heightVal - v.normal;o.vertex = UnityObjectToClipPos(v.vertex+ float4(offsetVal,1));o.growVal = growVal;return o;}half4 frag (v2f i) : SV_Target{half4 col = tex2D(_MainTex, i.uv);half3 diffuseCol = (1 - i.growVal)*_growCol + col.rgb;half4 alphaCol = tex2D(_AlphaMap, i.uv);float alpha = alphaCol.r*i.growVal;clip(alpha - 0.5);return half4(diffuseCol,alpha);}ENDCG}}
}
相关文章:
顶点程序经典案例——树木生长
树木生长Shader一、介绍 大家好,我是阿赵。这次来做一个树木生长的Shader。 顶点程序作为整个渲染管线里面和片段程序并列的两大可控过程之一,一直存在感都比较低。我们平时制作的效果,很多都是在片段程序里面实现的计算,顶点程序…...
在云计算环境下保护Java应用程序的有效措施
云计算(Cloud)技术是近年来计算机科学的一个重要突破。大多数组织已经通过将自己的应用程序移入云平台而获益。不过,如何保证应用程序在第三方服务器上的安全性,是一项艰巨的挑战。 在本文中,我们将重点讨论Java&…...
vscode-markdown-代码片段及快捷键设置
代码片段及快捷键设置 主要为了插入表格和图片标签节约一点输入时间 代码片段设置 ctrlshiftp 打开面板输入 configure user snippets选择markdowncopy如下设置放入{}中 "tb4*4": {"prefix": "tb4*4","body": ["| $1 | $2 | $…...
ModelNet40数据集
跑PointNet,modelnet40数据集时; 有些人直接用.off文件;——【CAD模型】普林斯顿形状Banchmark中的.off文件遵循以下标准: OFF文件全是以OFF关键字开始的ASCII文件。下一行说明顶点的数量、面片的数量、边的数量。 边的数量可以安全地省略。对模型不会有影响(可以为…...
【都2023年了,还在问网络安全怎么入门】
前言 【都2023年了,还在问网络安全怎么入门】所以这一期就出一一个怎么学习网络安全的学习路线和方法,觉得有用的话点赞收藏下 首先咱们聊聊,学习网络安全方向通常会有哪些问题 1、打基础时间太长 学基础花费很长时间,光语言都有…...
Apple Xcode 14.3 (14E222b) 正式版发布下载
Command Line Tools for Xcode 14, tvOS 16 & watchOS 9 Simulator Runtime 请访问原文链接:https://sysin.org/blog/apple-xcode-14/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org Xcode 14 包含了在所有 Ap…...
【Linux】sar常用选项介绍
sar 使用 安装sysstat apt-get install sysstat -y #或 yum install sysstat -y选项 用法: sar [ 选项 ] [ <时间间隔> [ <次数> ] ] 选项: [ -A ] [ -B ] [ -b ] [ -C ] [ -D ] [ -d ] [ -F [ MOUNT ] ] [ -H ] [ -h ] [ -p ] [ -q ] [ -r [ ALL ] ]…...
PHP 单笔转账到支付宝账户,支付宝公钥证书实现版本
支付宝某些业务只能使用公钥证书方式来验签 如:即使转账 红包等 笔者就要实现这样一个功能,【单笔转账到支付宝账户】,采用支付宝公钥证书签名来实现。 话不多说,流程先走起 第一步:下载支付宝秘钥生成器 由于我们使…...
第十四届蓝桥杯大赛软件赛省赛 C/C++ 大学 A 组 E 题
颜色平衡树问题描述格式输入格式输出样例输入样例输出评测用例规模与约定解析参考程序问题描述 格式输入 输入的第一行包含一个整数 n ,表示树的结点数。 接下来 n 行,每行包含两个整数 Ci , Fi,用一个空格分隔,表示第 i 个结点 …...
Python 小型项目大全 21~25
二十一、DNA 可视化 原文:http://inventwithpython.com/bigbookpython/project21.html 脱氧核糖核酸是一种微小的分子,存在于我们身体的每个细胞中,包含着我们身体如何生长的蓝图。它看起来像一对核苷酸分子的双螺旋结构:鸟嘌呤、…...
MinIO从信息泄漏到RCE
文章目录信息泄露漏洞利用漏洞分析漏洞修复RCE漏洞分析参考文章信息泄露 漏洞利用 如果MinIO以集群方式部署,存在信息泄露漏洞,攻击者可以通过HTTP请求获取目标进程的所有环境变量,包括MINIO_SECRET_KEY和MINIO_ROOT_PASSWORD. vulhub有环…...
202.Spark(九):SparkStreaming案例实操
目录 一、启动zookeeper,kafka基础环境 二、项目导好jar包,并且创建源数据,并在kafka中测试能否消费到数据...
GlusterFS(GFS)分布式文件系统
目录 一.文件系统简介 1.文件系统的组成 2.文件系统的作用 3.文件系统的挂载使用 二.GlusterFS概述 1.GlusterFS是什么? 2.GlusterFS的特点 3.GlusterFS术语介绍 3.1 Brick(存储块) 3.2 Volume(逻辑卷) 3.3…...
ChatGPT文本框再次升级,打造出新型操作系统
在ChatGPT到来之前,没有谁能够预见。但是,它最终还是来了,并引起了不小的轰动,甚至有可能颠覆整个行业。 从某种程度上说,ChatGPT可能是历史上增长最快的应用程序,仅在两个多月就拥有了1亿多活跃用户&…...
DPU02国产USB转UART控制芯片替代CP2102
目录DPU02简介DPU02芯片特性应用DPU02简介 DPU02是高度集成的USB转UART的桥接控制芯片,该芯片为RS-232设计更新为USB设计,并简化PCB组件空间提供了一个简单的解决方案。 DPU02包括了一个USB 2.0全速功能控制器、USB收发器、振荡器、EEPROM和带…...
Softing新版HART多路复用器软件支持西门子控制器
用于访问配置和诊断数据的HART多路复用器软件——Softing smartLink SW-HT,现在支持西门子的ET200远程IO和FDT/DTM接口。 smartLink SW-HT是一个基于Docker容器的软件应用。通过该软件,用户可以快速地访问以太网远程IO的HART设备,并且无需额外…...
〖Python网络爬虫实战⑫〗- XPATH语法介绍
订阅:新手可以订阅我的其他专栏。免费阶段订阅量1000python项目实战 Python编程基础教程系列(零基础小白搬砖逆袭) 说明:本专栏持续更新中,目前专栏免费订阅,在转为付费专栏前订阅本专栏的,可以免费订阅付费…...
实例方法、类方法、静态方法、实例属性、类属性
背景:今天在复习类相关知识的时候,突然想到这几种类型的方法的区别和用法,感觉有点模棱两可,于是总结一下,加深记忆。 定义:想要区别和理解几种方法,首先要定义一个类,要在类中加深…...
数据结构---二叉树
专栏:数据结构 个人主页:HaiFan. 专栏简介:这里是HaiFan.的数据结构专栏,今天的内容是二叉树。 二叉树树的概念及结构二叉树概念及结构二叉树的概念二叉树的存储结构二叉树的顺序结构及实现大根堆和小根堆堆的实现及其各个接口堆的…...
CMake——从入门到百公里加速6.7s
目录 一、前言 二、HelloWorld 三、CMAKE 界面 3.1 gui正则表达式 3.2 GUI构建 四 关键字 4.1 add_library 4.2 add_subdirectory 4.3 add_executable 4.4 aux_source_directory 4.5 SET设置变量 4.6 INSTALL安装 4.7 ADD_LIBRARY 4.8 SET_TARGET_PROPERTIES 4.9…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 发送和接收都必须准备好࿰…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
关于easyexcel动态下拉选问题处理
前些日子突然碰到一个问题,说是客户的导入文件模版想支持部分导入内容的下拉选,于是我就找了easyexcel官网寻找解决方案,并没有找到合适的方案,没办法只能自己动手并分享出来,针对Java生成Excel下拉菜单时因选项过多导…...
WPF八大法则:告别模态窗口卡顿
⚙️ 核心问题:阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程,导致后续逻辑无法执行: var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题:…...
pycharm 设置环境出错
pycharm 设置环境出错 pycharm 新建项目,设置虚拟环境,出错 pycharm 出错 Cannot open Local Failed to start [powershell.exe, -NoExit, -ExecutionPolicy, Bypass, -File, C:\Program Files\JetBrains\PyCharm 2024.1.3\plugins\terminal\shell-int…...
