顶点程序经典案例——树木生长

news2024/10/7 4:27:40

返回目录

树木生长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 off
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            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
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/412796.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构泛型

1.定义 先让我们看看官方是如何定义泛型的 是不是看起来不太容易,解释一下: 就是我们想有一种数据类型,它可以适用各种数据类型。从代码上讲就是对类型实现参数化。 2.引例 例:实现一个类,类中包含一个数组成员&…

直方图 颜色映射

文章目录hist map1. 原理2.灰度图3. 对于彩色图像4. 直方图规定化效果hist map 1. 原理 code:https://github.com/rossgoodwin/hmap 利用队列记录 hist src > tgt, src < tgt , src tgt的 索引。 然后&#xff0c;对于每个hist excess, 将其移动到 hist deficit 进行…

linux环境下,使用binlog模式恢复mysql数据(mysql数据库中的一张表误删了怎么找回?)。

linux环境下&#xff0c;使用binlog模式恢复mysql数据&#xff08;mysql数据库中的一张表误删了怎么找回&#xff1f;&#xff09;。 问题&#xff1a;linux中开启binlog模式下&#xff0c;mysql数据库中的一张表误删了怎么找回&#xff1f; 1.首先在mysql中查看是否开启binl…

气传导耳机是不是智商税?气传导耳机值得不值得入手?一文带你看懂

先说结论:并不是智商税。 目前的声音传播途径分为固体、气体和液体。而气传导耳机顾名思义&#xff0c;就是通过气体传声&#xff0c;这一点和我们日常使用的头戴式耳机、半入耳式入耳式耳机都是一样的&#xff0c;和我们日常生活中接触到最多的声音传播方式也是一样。 只不过…

动态规划概述

动态规划概述动态规划的两个要求&#xff1a; 1.最优子结构 例&#xff1a;现有一座10级台阶的楼梯&#xff0c;我们要从下往上走&#xff0c;每次只能跨一步&#xff0c;一步可以往上走1级或者2级台阶&#xff0c;请问一共有多少种解法呢&#xff1f; 台阶数12345678910走法数…

手动挡科目三道路驾驶技能考试及理论考试要点

路线每个驾校的科目三路线可能都不一样&#xff0c;但是考点基本差不多。我当时选的驾校是北京公交驾校&#xff0c;路线图如下&#xff1a;考试要点在考试大厅等待叫号&#xff0c;一般大屏都会公布xxx学员找xx号车考试&#xff0c;这边白色车是手动挡&#xff0c;灰色车是自动…

Web自动化测试-【Selenium环境部署Edge】

Selenium Web自动化测试工具 之前写过一篇关于自动化测试的博客&#xff0c;里面是有的chrome驱动&#xff0c;由于不适配缘故&#xff0c;更新以下Edge驱动。 自动化测试 Selenium环境部署 准备 Edge 浏览器准备 Edge 驱动包 a .查看自己的Edge浏览器版本&#xff08;浏览器版…

【论文解读】ConvNeXt V2: Co-designing and Scaling ConvNets with Masked Autoencoder

1. 本文贡献 提出了一个全卷积掩码的自动编码器框架和一个新的全局响应归一化&#xff08;GRN&#xff09;层 1.1 想法 本文的想法是希望能在 ConvNeXt 中使用MAE,但是MAE的设计架构是基于vision transformer的&#xff0c;与使用密集滑动窗口的标准ConvNets不兼容&#xf…

upload 通关pass16-pass20

1.pass16 白名单 二次渲染 需要先上传一个正常图片&#xff0c;然后下载下来&#xff0c;跟原图片进行比对&#xff0c;用010 16进制编辑器&#xff0c;把php代码放到没有改变的位置&#xff0c;即一样的地方 访问&#xff1a; 2.pass17 白名单 条件竞争 这题先是上传文件并…

音质蓝牙耳机哪款好用?2023公认音质好的四款蓝牙耳机推荐

现如今&#xff0c;蓝牙耳机越来越受欢迎&#xff0c;不少人在听歌、追剧、甚至是玩游戏的时候都会戴着它。最近看到很多人问&#xff0c;音质蓝牙耳机哪款好用&#xff1f;针对这个问题&#xff0c;我来给大家推荐四款公认音质好的蓝牙耳机&#xff0c;一起来看看吧。 一、南…

Nginx connect req access 模块

Nginx connect req access 模块演练 limig_conn模块&#xff1a;限制TCP连接数limit_req模块&#xff1a;限制请求频率access 模块&#xff08;allow/deny&#xff09;&#xff1a;限制ip段访问auth_request: 基于HTTP响应状态码做权限控制压测可以使用 postman的 run collect…

AndroidStudio如何进行手机应用开发?

文章目录0、引言1、AndroidStudio开发环境配置2、创建第一个手机应用0、引言 Android手机应用因其搭载于手机&#xff0c;使用便捷&#xff0c;应用被大量开发使用。笔者使用手机多年&#xff0c;用过许多手机软件&#xff0c;在使用的过程中&#xff0c;虽然手机软件能解决大部…

C++开发必知的内存问题及常用的解决方法-经典文章

1. 内存管理功能问题 由于C语言对内存有主动控制权&#xff0c;内存使用灵活和效率高&#xff0c;但代价是不小心使用就会导致以下内存错误&#xff1a; • memory overrun&#xff1a;写内存越界 • double free&#xff1a;同一块内存释放两次 • use after free&#xff1…

【数据结构】二叉树顺序结构及实现

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;初阶数据结构 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对…

Surfshark下载到使用完整教程|2023最新

2023年3月16日更新 在正式介绍surfshark的教程( 教程直达学习地址: qptool.net/shark.html )之前&#xff0c;我们可以来看看最近surfshark的服务与产品退化到什么程度了。我曾经是Surshark两年的忠实用户&#xff0c;但是&#xff0c;现在&#xff0c;作为一个负责人的测评&a…

PostMan动态参数及循环调用

最近需要在测试环境批量创建es索引&#xff0c;也就是某个接口需要循环调用且参数还是变化的&#xff0c;但是我又不想写代码和脚本&#xff0c;于是研究了一下postman一些好玩的功能&#xff0c;希望能节约大家的开发时间 一.设置请求参数 1.获取创建索引的请求以及参数&…

ELK+Filebeat日志分析系统

目录 一.ELK基本介绍 1.ELK是什么&#xff1f; 2.组件简介 2.1 ELK组件介绍 2.2 ELFK组件介绍 2.3 其它组件 4.使用ELK的原因 5.完整日志系统的基本特征 二.Elasticsearch的介绍 三.Logstash的介绍 四.Kibana的介绍 五.ELK的工作原理 六.部署ELK日志分析系统 1.环…

0基础学习软件测试有哪些建议

其实现在基础的资料和视频到处都是&#xff0c;就是看你有没有认真的去找学习资源了&#xff0c;去哪里学习都是要看你个人靠谱不靠谱&#xff0c;再好的教程和老师&#xff0c;你自己学习不进去也是白搭在正式选择之前&#xff0c;大可以在各种学习网站里面找找学习资源先自己…

springboot+vue动物园管理系统java

本系统使用的角色主要有系统管理员、注册用户&#xff0c;本系统分为系统前台和系统后台&#xff0c;首先在系统前台&#xff0c;游客用户可以经过账号注册&#xff0c;管理员审核通过后&#xff0c;用账号密码登录系统前台&#xff0c;查看论坛交流、动物展览、原生动物展览、…

HTML5 <head> 标签、HTML5 <i> 标签

HTML5 <head> 标签 实例 HTML5 <head> 标签表示文档的头部&#xff0c;其中包含了与该文档有关的信息&#xff01; 一份在头部带有 <title> 标签的 HTML 文档&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"utf-8&…