【译】Unity3D Shader 新手教程(3/6) —— 更加真实的积雪
本文为翻译,附上原文链接。
转载请注明出处——polobymulberry-博客园。
如果你满足以下条件,我建议你阅读这篇教程:
- 你想知道如何在表面着色器中进行混色(blend colour)
- 你想实现一个更加真实的积雪效果
引论
我觉得有雪区域向无雪区域过渡的有些突兀,感觉更像白色的油漆涂在了岩石上,而不是积雪!为了使我们积雪shader的效果更加完美所以下一步需要做的是允许积雪和岩石纹理同时进行渲染,从而达到混色的效果。
我们只要对表面着色器的pixel处理方式进行一些修改就可以达到很好的积雪效果,而且这也将证明saturate函数是非常有用的。
准备工作
教程第二部分中的_SnowLevel(表示积雪程度的变量)决定了该像素是否应该赋以积雪的颜色,此处我们也会使用_SnowLevel来使积雪的边缘颜色变为半透明的白色。而不是之前完全的白色。当该像素的法向与雪落下的方向越一致(即两者夹角越小),则该像素的不透明度越高,换句话说,就是此处积雪更多,直到完全不透明,此处的像素颜色值将变为白色(事实上就是变成了积雪的颜色)。
实现该Shader
和教程第二部分的shader最大的差别在于,第二部分的shader中,每个像素的颜色值要不是积雪颜色,要不就是纹理颜色,而此部分shader将此非黑即白的判断改进成颜色值的变化过程。这导致我们将重写了shader的逻辑部分,并使用数学计算的方法来代替if的条件判断。
首先我们需要一个属性值(Property)来表示我们混合积雪颜色的程度。我们给该属性值取名为_Wetness(湿润度,如果该值越大,则混色中积雪颜色所占比例越低,这表明积雪越湿润,则雪的颜色越少,都化成水了):
_Wetness ("Wetness", Range(0, 0.5)) = 0.3
接着我们需要一个变量在shader中表示该属性值
float _Wetness;
我们使用下面这个公式计算difference变量。
float difference = dot(WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) - lerp(1,-1,_Snow);
其实第二部分的代码中也可以使用difference变量(比如当difference大于0就将该区域赋以积雪颜色,小于0就使用原先纹理颜色),关键在于对于difference的处理和应用不同。
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Normal = UnpackNormal (tex2D (_Bump, IN.uv_Bump));
float difference = dot(WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) - lerp(1,-1,_Snow);;
difference = saturate(difference / _Wetness);
o.Albedo = difference*_SnowColor.rgb + (1-difference) *c;
o.Alpha = c.a;
}
我们可以看到计算完difference后,首先将difference除以_Wetness(此处我们可以一窥_Wetness的具体用途),然后对difference使用saturate函数。saturate函数的具体用处如下:
- saturate函数将给定的值规范到0~1之间(大于1的置为1,小于0的置为0,其他不变)
- 所以当difference小于0时,即此处无积雪,我们通过saturate函数置difference为0。
- 如果_Wetness的默认值为0.3,并且法向和落雪方向夹角在73度~90度之间,则difference的值健在0~1之间,大于90度,则为0,小于73度则为1.
一个余弦值的范围是1到-1,差距为2,代表了0度到180度的变化(一直从同向变化到反向),所以要是difference在0~1之间,则点乘的结果(即cos值)必须在0~0.3之间,所以对应夹角为73度~90度(cos73=0.3,cos90=1)。
然后我们用difference乘以积雪颜色,difference代表积雪颜色在最终颜色所占的比例。我们再设置纹理本身颜色的比重为1-difference,相加得到最终颜色。其中夹角小于73度的区域将全部填充为积雪颜色,在73度~90度之间(也就是积雪的边缘)有一积雪过渡效果,大于90度的将使用纹理原来颜色。
o.Albedo = difference*_SnowColor.rgb + (1-difference) *c;
修正顶点数据
如果我们的的雪十分湿润(wet),那么只使用原先的shader的话,我们会发现积雪少的区域,模型也会增厚,根本原因是我们的顶点变化方式没有随积雪混色方式(主要是添加了_Wetness参数)变化而变化,产生的效果将不真实。所以我们应该将参数_Wetness应用到计算中,使模型的增厚效果随_Wetness变化。
void vert (inout appdata_full v) {
if(dot(v.normal, _SnowDirection.xyz) >= lerp(1,-1, ((1-_Wetness) * _Snow*2)/3))
{
v.vertex.xyz += (_SnowDirection.xyz + v.normal) * _SnowDepth * _Snow;
}
}
可以看到,我们决定是否增厚模型的判断变成了是否大于
lerp(1,-1, ((1-_Wetness) * _Snow*2)/3))
如果_Wetness为0,则一切都没变化,如果_Wetness变到了其最大范围0.5,则_Snow所占的比例不再是上文的2/3,而是1/3了 - 使模型增厚范围增加了50%。
下面是最终的效果图:

源代码
资源和代码在这里。
Shader "Custom/Realistic Snow" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Bump ("Bump", 2D) = "bump" {}
_Snow ("Snow Level", Range(0,1) ) = 0
_SnowColor ("Snow Color", Color) = (1.0,1.0,1.0,1.0)
_SnowDirection ("Snow Direction", Vector) = (0,1,0)
_SnowDepth ("Snow Depth", Range(0,0.2)) = 0.1
_Wetness ("Wetness", Range(0, 0.5)) = 0.3
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert vertex:vert
sampler2D _MainTex;
sampler2D _Bump;
float _Snow;
float4 _SnowColor;
float4 _SnowDirection;
float _SnowDepth;
float _Wetness;
struct Input {
float2 uv_MainTex;
float2 uv_Bump;
float3 worldNormal;
INTERNAL_DATA
};
void vert (inout appdata_full v) {
//将_SnowDirection转化到模型局部坐标系下
float4 sn = mul(UNITY_MATRIX_IT_MV, _SnowDirection);
if(dot(v.normal, sn.xyz) >= lerp(1,-1, (_Snow*2)/3))
{
v.vertex.xyz += (sn.xyz + v.normal) * _SnowDepth * _Snow;
}
}
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Normal = UnpackNormal (tex2D (_Bump, IN.uv_Bump));
half difference = dot(WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) - lerp(1,-1,_Snow);
difference = saturate(difference / _Wetness);
o.Albedo = difference*_SnowColor.rgb + (1-difference) *c;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
【译】Unity3D Shader 新手教程(3/6) —— 更加真实的积雪的更多相关文章
- 【译】Unity3D Shader 新手教程(1/6)
本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 刚开始接触Unity3D Shader编程时,你会发现有关shader的文档相当散,这也造成初学者对Unity3D ...
- 【译】Unity3D Shader 新手教程(2/6) —— 积雪Shader
本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 如果你是一个shader编程的新手,并且你想学到下面这些酷炫的技术,我觉得你可以看看这篇教程: 实现一个积雪效果的 ...
- 【译】Unity3D Shader 新手教程(6/6) —— 更好的卡通Shader
本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 动机 如果你想了解以下几件事,我建议你阅读以下这篇教程: 想知道如何写一个multipass的toon shade ...
- 【译】Unity3D Shader 新手教程(5/6) —— Bumped Diffuse Shader
本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 动机 如果你满足以下条件,我建议你阅读这篇教程: 你想学习片段着色器(Fragment Shader). 你想实现 ...
- 【译】Unity3D Shader 新手教程(4/6) —— 卡通shader(入门版)
本文为翻译,附上原文链接. 转载请注明出处--polobymulberry-博客园. 暗黑系 动机 如果你满足以下条件,我建议你阅读这篇教程: 你想了解更多有关表面着色器的细节知识. 你想实现一个入门 ...
- Unity3D Shader基础教程
原文地址:http://bbs.9ria.com/thread-212557-1-1.html 此教程将指引你如何建立自己的Shaders,让你的游戏场景看起来更好.Unity配备了强大的阴影和材料的 ...
- Unity3D Shader官方教程翻译(十九)----Shader语法,编写表面着色器
Writing Surface Shaders Writing shaders that interact with lighting is complex. There are different ...
- 【译】Meteor 新手教程:在排行榜上添加新特性
原文:http://danneu.com/posts/6-meteor-tutorial-for-fellow-noobs-adding-features-to-the-leaderboard-dem ...
- 【浅墨Unity3D Shader编程】之二 雪山飞狐篇:Unity的基本Shader框架写法&颜色、光照与材质
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/40955607 作者:毛星云(浅墨) ...
随机推荐
- 对抗密码破解 —— Web 前端慢 Hash
(更新:https://www.cnblogs.com/index-html/p/frontend_kdf.html ) 0x00 前言 天下武功,唯快不破.但在密码学中则不同.算法越快,越容易破. ...
- runtime梳理。
一.runtime简介 RunTime简称运行时.OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制. 对于C语言,函数的调用在编译的时候会决定调用哪个函数. 对于OC的函数,属于 ...
- 6. ModelDriven拦截器、Preparable 拦截器
1. 问题 Struts2 的 Action 我们将它定义为一个控制器,但是由于在 Action 中也可以来编写一些业务逻辑,也有人会在 Action 输入业务逻辑层. 但是在企业开发中,我们一般会将 ...
- c#多线程
一.使用线程的理由 1.可以使用线程将代码同其他代码隔离,提高应用程序的可靠性. 2.可以使用线程来简化编码. 3.可以使用线程来实现并发执行. 二.基本知识 1.进程与线程:进程作为操作系统执行程序 ...
- ecshop验证码
<?php //仿制ecshop验证码(四位大写字母和数字.背景) //处理码值(四位大写字母和数字组成) //所有的可能的字符集合 $chars = 'ABCDEFGHIJKLMNOPQRST ...
- Canvas讲解
1.Canvas是什么? 简单地说canvas是画布,可以进行画任何的线.图形.填充等一系列的操作,而且操作的画图就是js, 提供简单的二维矢量绘图. 2.步骤: <canvas id=&quo ...
- jQuery遮罩层登录对话框
用户登录是许多网站必备的功能.有一种方式就是不管在网站的哪个页面,点击登录按钮就会弹出一个遮罩层,显示用户登录的对话框.这用方式比较灵活方便.而现在扫描二维码登录的方式也是很常见,例如QQ.微信.百度 ...
- Atitit.软件开发的三层结构isv金字塔模型
Atitit.软件开发的三层结构isv金字塔模型 第一层,Implements 层,着重与功能的实现.. 第二次,spec层,理论层,设计规范,接口,等.流程.方法论 顶层,val层,价值观层,原则, ...
- CentOS 7 安装出现 /dev/root does not exits 导致无法安装的问题
本人在官网下的是这个 CentOS-7-x86_64-DVD-1611.iso ,然后用UltraISO 9.6制作的U盘启动盘,不过在安装的时候出现了这个错误, 然后也是搜了好久,试了一下,下面这个 ...
- appium+robotframework环境搭建
appium+robotframework环境搭建步骤(Windows系统的appium自动化测试,只适用于测试安卓机:ios机需要在mac搭建appium环境后测试) 搭建步骤,共分为3部分: 一. ...