【译】Unity3D Shader 新手教程(3/6) —— 更加真实的积雪

如果你满足以下条件,我建议你阅读这篇教程:

  • 你想知道如何在表面着色器中进行混色(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"
}
时间: 2024-10-21 20:31:53

【译】Unity3D Shader 新手教程(3/6) —— 更加真实的积雪的相关文章

【译】Unity3D Shader 新手教程(1/6)

刚开始接触Unity3D Shader编程时,你会发现有关shader的文档相当散,这也造成初学者对Unity3D Shader编程望而却步.该系列教程的第一篇文章(译者注:即本文,后续还有5篇文章)详细介绍了Unity3D中的表面着色器(Surface Shader)的,为学习更复杂的Shader编程打下基础. 动机 如果你是刚刚接触Shader编程的新手,你可能不知道从何开始踏出Shader编程的第一步.本教程将带你一步步完成一个表面着色器(Surface Shader)和片段着色器(Fra

【译】Unity3D Shader 新手教程(2/6) —— 积雪Shader

如果你是一个shader编程的新手,并且你想学到下面这些酷炫的技术,我觉得你可以看看这篇教程: 实现一个积雪效果的shader 创建一个具有凹凸纹理的shader 为每个像素修改其对应纹理值 在表面着色器中修改模型的顶点数据 引论 这是我们系列教程的第二部分,我们将在此部分实现些有用的技术.在学习完第一部分的所有背景知识后,我们将利用所学的知识实现一个简单的积雪效果的shader.效果如下: 准备工作 我们想做的其实很简单,简单介绍一下: 随着Snow Level(表示积雪的程度,该值越大,积雪

【译】Unity3D Shader 新手教程(6/6) —— 更好的卡通Shader

动机 如果你想了解以下几件事,我建议你阅读以下这篇教程: 想知道如何写一个multipass的toon shader. 在shader中学习更多不同参考坐标系(空间space)以及其作用. 深入学习一个实用的fragment shader. 学习矩阵相乘和Unity内建矩阵的使用. 该教程比第五篇教程更实用. 准备工作 为了实现一个描边的toon shader,我们需要做的是: 为模型描边. 将第四篇文章中的介绍的toon shader(使用的是surface shader)移植到vertex&

【译】Unity3D Shader 新手教程(5/6) —— Bumped Diffuse Shader

动机 如果你满足以下条件,我建议你阅读这篇教程: 你想学习片段着色器(Fragment Shader). 你想实现复杂的多通道着色器(multipass),但是对其不是很了解. 你想使用上面提到的两种技术(片段着色器和多Pass)来实现描边效果的Toon shader,你就需要理解这两种技术的概念. 学习资源 Martin Kraus's fantastic Wiki Book GLSL Programming/Unity 引论 在教程的第4部分,我们创建了一个相当好的toon shader,该

【译】Unity3D Shader 新手教程(4/6) —— 卡通shader(入门版)

暗黑系 动机 如果你满足以下条件,我建议你阅读这篇教程: 你想了解更多有关表面着色器的细节知识. 你想实现一个入门级别的卡通效果shader(Toon Shader). 你想知道渐变纹理(ramp texture)的使用方式. 你想了解边缘光照(rim lighting)的知识. 准备工作 我们想实现一个toon shader - 一种能让模型看起来具有卡通效果的shader,在图形学领域,这被称作非真实感图形学(Non Photorealistic Rendering). 为了实现这种卡通效果

Unity3D Shader官方教程翻译(十九)----Shader语法,编写表面着色器

Writing Surface Shaders Writing shaders that interact with lighting is complex. There are different light types, different shadow options, different rendering paths (forward and deferred rendering), and the shader should somehow handle all that compl

Unity3D Shader官方教程翻译(十一)----Shader语法:Pass的Blending(混合)

ShaderLab syntax: Blending 混合 Blending is used to make transparent objects. 混合是用来制作透明物体的. When graphics are rendered, after all shaders have executed and all textures have been applied, the pixels are written to the screen. How they are combined with

【浅墨Unity3D Shader编程】之二 雪山飞狐篇:Unity的基本Shader框架写法&颜色、光照与材质

本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/40955607 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 邮箱: [email protected] 本篇文章中,我们学习了Unity Shader的基本写法框架,以及学习了Shader中Properties(属性)的详细写法,光照.材质与颜色的具体写法.写了6个Shader作为本文S

Unity3D shader简介

Unity3D shader简介 可以肯定的说Unity3D使得很多开发者开发游戏更容易.毫无疑问,shader(着色器)编码,仍有很长的路要走.shader是一个专门运行在GPU的程序,经常被神秘包围,它最终绘制3D模型的三角形.如果你想给游戏一个特殊的显示,学习如何编写shader是必要的.Unity3D使用shader做后期处理,对2D游戏也是必不可少的.这个系列的文章将逐步介绍shader编程,并面向几乎没有任何shader知识的开发者. 简介 下图大致表示了在Unity3D渲染流程中发