Unity大面积草地渲染——1、Shader控制一棵草的渲染

news2024/11/25 14:54:51

大家好,我是阿赵。
这里开始讲大面积草地渲染的第一个部分,一棵草的渲染。按照惯例,完整shader在最后。前面是原理的介绍。

一、准备的资源

在这里插入图片描述

这里我自己随便做了一个草的模型,主要是用几个面片搭建的一个简单模型。
在这里插入图片描述
在这里插入图片描述

然后我准备了一张草的贴图,带有透明通道的。
在这里插入图片描述

把贴图赋予给模型后,在3DsMax里面草是这个样子,只是简单的几个直草。

二、控制草的形态

把草的模型和贴图放到Unity引擎里面,写一个最简单的漫反射加上Cutout的shader,会看到草是这个样子:
在这里插入图片描述

这里我们需要根据草的顶点坐标,做2个范围的控制:

1.从底部到顶部的顶点渐变
在这里插入图片描述

由于我做模型的时候,模型的中心点就在草的根部,所以我可以拿模型中心点作为一个基准,然后计算其他顶点的坐标离根部的Y坐标差,算出一个渐变。上图是越高的地方颜色越白。
通过这个,我们在写Shader的时候,就可以根据顶点高度的不同,给草赋予不同的表现,比如颜色的变化,或者弯曲度的变化。
为了控制黑白渐变的范围,我一般会加一个smoothstep来控制。
2.以底部为圆心,求扩散的范围
在这里插入图片描述

这个计算和刚才的高度计算效果不一样,离圆心越近的点越黑,离得越远的点越白。这个渐变的范围,可以用于我们计算时,草离中心不一样,表现不一样。
同样的,为了控制黑白渐变的范围,我会加smoothstep控制。

有了以上2个渐变之后,下面就开始来计算草的外观了。

1、草的颜色变化

一棵植物,一般会在不同的生长部位表现出颜色不一样,比如一棵草,按道理可能会根部的颜色较深,而顶部的地方颜色较浅。
这里先实现一下这个效果,根据刚才的高度渐变计算结果,可以把草本身的贴图颜色再混合2个颜色:一个根部颜色和一个顶部颜色。
混合之后,结果如下:
在这里插入图片描述

2、草的弯曲度变化

之前在3DsMax里面做的草模型,是直挺挺的草,完全没有弯曲度的,但实际上的草,会因为受到重力的原因,而到了一定的长度和角度之后出现弯曲。
这里我用到了上面计算的高度渐变和圆心渐变,来做这个效果。
首先,根据实际情况,离圆心越远的叶子,由于他倾斜度会越大,所以受到重力之后他应该往下弯曲得更多,然后,究竟从哪个高度开始才应该弯曲呢?这里就可以用高度渐变来控制了。
最后得到的结果是这样:
在这里插入图片描述

三、关于阴影

由于我是用顶点片段程序来写这个shader的,所以阴影需要用单独一个pass来绘制。
怎样使用动态阴影,之前我有介绍过,要在shader里面加入影子三剑客。
不过这里由于使用了顶点变化的控制,所以在这个专门绘制影子的pass里面,我们需要把顶点程序也照抄一份。
还有一个需要注意的是,在顶点程序里面照抄完控制顶点的代码之后,不是给v2f结构体赋值转换到裁剪空间的顶点坐标,而是直接赋值给输入顶点程序的appdata结构体的vertex。这是比较特殊的地方。

四、完整shader:

Shader "azhao/GrassBsse"
{
    Properties
    {
		_MainTex("MainTex", 2D) = "white" {}
		_hmin("hmin", Range(0 , 1)) = 0
		_hmax("hmax", Range(0 , 1)) = 1
		_hOffset("hOffset", Range(-1 , 1)) = 0
		_vmin("vmin", Range(0 , 1)) = 0
		_vmax("vmax", Range(0 , 1)) = 1
		_vOffset("vOffset", Range(-5 , 5)) = 0
		_topCol("topCol", Color) = (0,1,0,0)
		_bottomCol("bottomCol", Color) = (0,0,0,0)

    }
    SubShader
    {
		Tags{"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout" }
		Cull Off

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag


			#include "UnityShaderVariables.cginc"
			#pragma target 3.0
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {                
                float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 centerPos : TEXCOORD1;
				float3 worldPos : TEXCOORD2;
				float3 hvVal : TEXCOORD3;

            };

			uniform float _hmin;
			uniform float _hmax;
			uniform float _vmin;
			uniform float _vmax;
			uniform float _vOffset;

			uniform float _hOffset;

			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			uniform float4 _topCol;
			uniform float4 _bottomCol;
			SamplerState sampler_MainTex;

            v2f vert (appdata v)
            {
                v2f o;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.centerPos = mul(unity_ObjectToWorld, float4(float3(0, 0, 0), 1)).xyz;
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				float hVal = smoothstep(_hmin, _hmax, o.worldPos.y - o.centerPos.y);
				float vVal = smoothstep(_vmin, _vmax, distance(o.worldPos.xz, o.centerPos.xz));
				float hvVal = hVal * vVal;
				o.hvVal = float3(hVal, vVal, hvVal);
				float hVertexOffset = hvVal * _hOffset;
				float2 vVertexOffset = (o.worldPos.xz - o.centerPos.xz)*hvVal*_vOffset;

				o.pos = UnityObjectToClipPos(v.vertex+float3(vVertexOffset.x, hVertexOffset, vVertexOffset.y));
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
                half4 col = tex2D(_MainTex, i.uv);
				half3 finalCol = col.rgb * _topCol.rgb*i.hvVal.z + col.rgb;
				finalCol = clamp(finalCol*i.hvVal.x + _bottomCol * (1 - i.hvVal.x)*finalCol,  half3(0, 0, 0), half3(1, 1, 1));
				half alpha = col.a;
				clip(alpha - 0.5);
                return half4(finalCol,alpha);
            }
            ENDCG
        }
		
		//为了产生影子,加多一个pass
		Pass {
			Name "ShadowCaster"
			Tags { "LightMode" = "ShadowCaster" }

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 2.0
			#pragma multi_compile_shadowcaster
			#include "UnityCG.cginc"

			struct v2f {
				V2F_SHADOW_CASTER;
			};
			uniform float _hmin;
			uniform float _hmax;
			uniform float _vmin;
			uniform float _vmax;
			uniform float _vOffset;

			uniform float _hOffset;
			uniform sampler2D _MainTex;
			uniform float4 _MainTex_ST;
			uniform float4 _topCol;
			uniform float4 _bottomCol;
			SamplerState sampler_MainTex;
			v2f vert(appdata_base v)
			{
				v2f o;
				float3 centerPos = mul(unity_ObjectToWorld, float4(float3(0, 0, 0), 1)).xyz;
				float3 worldPos = mul(unity_ObjectToWorld, v.vertex);
				float hVal = smoothstep(_hmin, _hmax, worldPos.y - centerPos.y);
				float vVal = smoothstep(_vmin, _vmax, distance(worldPos.xz, centerPos.xz));
				float hvVal = hVal * vVal;
				float hVertexOffset = hvVal * _hOffset;
				float2 vVertexOffset = (worldPos.xz - centerPos.xz)*hvVal*_vOffset;

				v.vertex = v.vertex + float4(vVertexOffset.x, hVertexOffset, vVertexOffset.y,1);
				TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
				return o;
			}

			float4 frag(v2f i) : SV_Target
			{
				SHADOW_CASTER_FRAGMENT(i)
			}
			ENDCG

		}

    }
	FallBack "VertexLit"
}

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

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

相关文章

DAP之FLM算法研究

本人所写的博客都为开发之中遇到问题记录的随笔,主要是给自己积累些问题。免日后无印象,如有不当之处敬请指正(欢迎进扣群 24849632 探讨问题); 写在专栏前面https://blog.csdn.net/Junping1982/article/details/129955766 玩过自制DAP工具的一定都知道通过MDK目录的FLM文…

priority_queue

priority_queue&#xff1a;优先队列 头文件还是 < queue> 本质就是堆&#xff1a;完全二叉树 条件&#xff08;任意节点都比其孩子大&#xff08;大根堆&#xff09;&#xff09; priority_queue的默认比较是less&#xff0c;但是建出来的是大根堆&#xff1b;sort排序…

顺序表的基本操作(初始化,增,删,查,改等等)

1.线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串...线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直线…

智能算法系列之蚁群算法

本博客封面由ChatGPT DALLE 2共同创作而成。 文章目录 前言1. 算法思想2. 算法流程3. 细节梳理4. 算法实现4.1 问题场景4.2 代码实现 代码仓库&#xff1a;IALib[GitHub] 前言 本篇是智能算法(Python复现)专栏的第五篇文章&#xff0c;主要介绍蚁群算法(Ant Colony Optimizati…

cmd@快捷键方式@静默执行命令

文章目录 ref快捷方式执行命令行或打开文件eg:直接打开某个文件 创建快捷方式eg:快捷方式运行命令 ref How can I execute a Windows command line in background? - Super Userstbrenner/SilentCMD: SilentCMD executes a batch file without opening the command prompt wi…

如何用100天时间,让CSDN的粉丝数从0狂飙到10000

2022年10月7日&#xff0c;正式开通了CSDN账号。但因为工作忙的原因&#xff0c;一直没有时间写博客文章&#xff0c;也没有投入精力在CSDN上。理所当然的&#xff0c;我的粉丝数量很稳定&#xff0c;一直保持着0的记录。 2023年春节假期过后&#xff0c;有点空闲时间了&#x…

Tre靶场通关过程(linpeas使用+启动项编辑器提权)

Tre靶场通关 通过信息收集获得到了普通用户账号密码&#xff0c;利用PEASS-ng的linpeas脚本进行提权的信息收集&#xff0c;根据已有信息进行提权。 靶机下载地址&#xff1a; https://download.vulnhub.com/tre/Tre.zip 信息收集 靶机IP探测&#xff1a;192.168.0.129 a…

java多线程下

ThreadLocal ThreadLocal 有什么用&#xff1f;通常情况下&#xff0c;我们创建的变量是可以被任何一个线程访问并修改的。如果想实现每一个线程都有自己的专属本地变量该如何解决呢&#xff1f;JDK 中自带的ThreadLocal类正是为了解决这样的问题。 ThreadLocal类主要解决的就…

解释表情包This code will never do anything!

目录 解释一下下面这段代码 #include int main(){ while (1) ;} void unreachable(){ std::cout <<"Hello world!"<;}<> 解释一下下面这段代码 $ clang loop.cpp -01 -Wall -o loop $ ./loop Hello world! 解释一下下面这段代码 #include <iostre…

python面试题

文章目录 赋值、深拷贝和浅拷贝有什么区别&#xff1f;元组和列表有什么不同&#xff1f;和is有什么不同&#xff1f;集合怎么转字典&#xff1f;字典怎么遍历&#xff1f;如何在Python中实现多线程&#xff1f;如何实现tuple和list的转换&#xff1f;实现删除一个list里面的重…

厉害的人是搭建平台的人,少数人

牛人是搭建平台的人&#xff0c;是少数人 创建平台才有难度 趣讲大白话&#xff1a;多数人的成就是借平台之力 【趣讲信息科技157期】 **************************** 认识清楚平台能力和个人能力 创建阿里巴巴公司太难 多数人是借阿里巴巴平台之力成就的 离开一个成功的平台&a…

汽车之家股票回购速度远低于预期,面临严重的资本投资风险

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 汽车之家的估值 基于对汽车之家&#xff08;ATHM&#xff09;2023财年非GAAP调整后每股收益2.55美元的普遍预测&#xff0c;市场给予汽车之家目前的估值约为2023财年预期正常化市盈率的13倍&#xff0c;猛兽财经认为这个市…

libevent高并发网络编程 - 01_libevent事件Event处理

文章目录 1. libevent事件驱动和事件处理简介2. 事件状态分析3. 事件Event常用API3.1 event_base_new()3.2 event_base_free()3.3 event_new()3.4 event_add()3.5 event_del()3.6 event_free()3.7 event_base_dispatch()3.8 event_base_loopbreak()3.9 evsignal_new()3.10 even…

继承与虚函数练习

Tip1 基类私有成员变量在子类中都不能直接访问&#xff0c;不是因为没有被子类继承&#xff0c;而是权限问题 Tip2 满足多态的父子对象&#xff0c;父类对象和子类对象前4个字节都是虚表指针&#xff08;vs2019下&#xff09;&#xff0c;父类与子类指向的是各自的虚表。 Tip…

云计算中的容器技术及其实践案例

第一章&#xff1a;什么是容器技术 随着云计算和DevOps的普及&#xff0c;容器技术在IT行业中越来越受到关注。容器是一种轻量级、可移植、可扩展的应用程序封装技术&#xff0c;可以将应用程序及其所有依赖项打包到一个独立的可执行文件中。相对于虚拟机技术&#xff0c;容器技…

无界AI绘画基础教程,和Midjourney以及Stable Diffusion哪个更好用?

本教程收集于&#xff1a;AIGC从入门到精通教程汇总 简单的总结 Midjourney&#xff0c;Stable Diffusion&#xff0c;无界AI的区别&#xff1f; Midjourney&#xff0c;收费&#xff0c;上手容易&#xff0c;做出来高精度的图需要自己掌握好咒语。咒语写不好&#xff0c;像…

【LLM大模型】LLM模型指令微调(更新中)

note 文章目录 note零、AIGC生成式模型1. 核心要素2. LLM evolutionary tree 二、LLM大模型1. ChatGLM&#xff08;1&#xff09;GLM-130B&#xff08;2&#xff09;ChatGLM-6B 2. LLaMA3. Chinese-LLaMA-Alpace4. Bloom5. PaLM 三、模型指令微调Reference 零、AIGC生成式模型 …

算法记录 | Day53 动态规划

1143.最长公共子序列 思路&#xff1a; 本题和动态规划&#xff1a;718. 最长重复子数组 (opens new window)区别在于这里不要求是连续的了&#xff0c;但要有相对顺序&#xff0c;即&#xff1a;“ace” 是 “abcde” 的子序列&#xff0c;但 “aec” 不是 “abcde” 的子序…

搭建DHCP、PXE、DNS、HTTP以及NFS服务综合实验的超详细讲解

文章目录 1.实验要求2.实验步骤2.1 步骤解答问题&#xff08;1&#xff09;2.1 步骤解答问题&#xff08;2&#xff09;2.1 步骤解答问题&#xff08;3&#xff09;2.1 步骤解答问题&#xff08;4&#xff09;2.1 步骤解答问题&#xff08;5&#xff09; 1.实验要求 &#xff…

Hadoop入门篇01---基础概念和部署教程

Hadoop入门篇01---基础概念和部署教程 Hadoop是什么Hadoop发展史Hadoop特点有哪些Hadoop版本Hadoop架构Hadoop 3.0新特性 Hadoop集群搭建集群简介集群部署方式standalone mode&#xff08;独立模式&#xff09;Pseudo-Distributed mode&#xff08;伪分布式模式&#xff09;Clu…