博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Unity Shaders】Reflecting Your World —— Unity3D中的遮罩反射(Masking Reflections)
阅读量:7153 次
发布时间:2019-06-29

本文共 2911 字,大约阅读时间需要 9 分钟。

本系列主要参考一书(感谢原书作者),同时会加上一点个人理解或拓展。

是本书所有的插图。是本书所需的代码和资源(当然你也可以从下载)。

========================================== 分割线 ==========================================

写在前面

有时候,我们并不想让物体的所有部分都反射,例如一个物体可能某些部分是玻璃材质的可以反射,而有些是塑料材质就不会反射。

在这篇教程里,我们将会学习一种技术来控制反射范围,这是通过一张texture作为遮罩(mask)来实现的。也就是说,我们可以使用一张texture的灰度值去决定该平面该如何反射,这意味着,一个为黑色的灰度值对应一个不会反射的子平面,而一个白色的灰度值对应一个完全反射的子平面。如今,基本所有的游戏制作都是使用这种方法来控制反射效果的。

下面,我们来看看在Unity里怎么使用Surface Shaders来实现它。

准备工作

  1. 首先,我们需要一个Cubemap,你可以生成一个新的或者使用用到的Cubemap。本教程所用的Cubemap如下(在中可以找到):
  2. 我们还需要一个texture来描述我们对象的那些部分是可以反射的,而哪些不可以。记住,黑色表示没有任何反射性,而白色表示可以完全反射。下面的图片是我们将会用到的texture:
  3. 最后,我们需要创建一个新的场景以及场景中的一个对象、平面和一个平行光,来让我们观察反射效果。除此之外,新建一个Shader和对应的Material,并命名为MaskedReflection。

实现

  1. 添加新的Properties:
    Properties {		_MainTex ("Base (RGB)", 2D) = "white" {}		_MainTint ("Diffuse Tint", Color) = (1,1,1,1)		_ReflAmount ("Reflection Amount", Range(0, 1)) = 1		_Cubemap ("Cubemap", CUBE) = ""{}		_ReflMask ("Reflection Mask", 2D) = ""{}	}
  2. SubShader块中添加它们的引用变量:
    CGPROGRAM		#pragma surface surf Lambert		sampler2D _MainTex;		sampler2D _ReflMask;		samplerCUBE _Cubemap;		float4 _MainTint;		float _ReflAmount;
  3. 修改Input结构体:
    struct Input {			float2 uv_MainTex;			float3 worldRefl;		};
  4. 修改surf函数:
    void surf (Input IN, inout SurfaceOutput o) {			half4 c = tex2D (_MainTex, IN.uv_MainTex);			float3 reflection = texCUBE(_Cubemap, IN.worldRefl).rgb;			float4 reflMask = tex2D(_ReflMask, IN.uv_MainTex);				o.Albedo = c.rgb * _MainTint;			o.Emission = (reflection * reflMask.r) * _ReflAmount;			o.Alpha = c.a;		}
最后整体代码如下:
Shader "Custom/MaskedReflection" {	Properties {		_MainTex ("Base (RGB)", 2D) = "white" {}		_MainTint ("Diffuse Tint", Color) = (1,1,1,1)		_ReflAmount ("Reflection Amount", Range(0, 1)) = 1		_Cubemap ("Cubemap", CUBE) = ""{}		_ReflMask ("Reflection Mask", 2D) = ""{}	}	SubShader {		Tags { "RenderType"="Opaque" }		LOD 200				CGPROGRAM		#pragma surface surf Lambert		sampler2D _MainTex;		sampler2D _ReflMask;		samplerCUBE _Cubemap;		float4 _MainTint;		float _ReflAmount;		struct Input {			float2 uv_MainTex;			float3 worldRefl;		};		void surf (Input IN, inout SurfaceOutput o) {			half4 c = tex2D (_MainTex, IN.uv_MainTex);			float3 reflection = texCUBE(_Cubemap, IN.worldRefl).rgb;			float4 reflMask = tex2D(_ReflMask, IN.uv_MainTex);				o.Albedo = c.rgb * _MainTint;			o.Emission = (reflection * reflMask.r) * _ReflAmount;			o.Alpha = c.a;		}		ENDCG	} 	FallBack "Diffuse"}
将场景中球体对应的材质设置如下图所示:
最后效果如图,其中左面的球体使用了遮罩反射,对比右面没有使用遮罩反射:

解释

这个Shader非常的简单,仅仅使用了texCUBE函数在Cubemap中采样。这个函数是内置的CGFX函数,它提供给我们一个Cubemap中的颜色值,然后我们将该值应用到平面上。Unity通过Input结构体中的worldRefl变量来帮我们在Cubemap中找到对应的采样位置。正如解释的一样,这个属性将会把摄像机视角的反射向量传递给我们。
一旦我们知道了反射元素(即反射的颜色值),我们就需要接着去采样我们的遮罩贴图。我们可以使用tex2D函数来完成它,这个函数在中就接触过。
当我们知道了两个textures对应的值后,我们就可以把Cubemap的颜色值乘以反射贴图的颜色值,并传递给o.Emission。最后,为了可以全局控制反射密度,我们需要把结果再乘以_ReflAmount属性。这可以帮我们控制平面的整体反射量(越大表明反射度越高,越接近镜子的效果)。
下面展示了不同的_ReflAmount值对应的不同的反射效果:

转载于:https://www.cnblogs.com/xiaowangba/p/6314698.html

你可能感兴趣的文章
在dos下运行java jar包,并把命令存为bat文件
查看>>
<如何成为一个成功的职业经理人>读书笔记2
查看>>
Java EE企业系统性能问题的原因和解决建议[也适用于.NET]
查看>>
IBM 云计算 笔记
查看>>
GNU make manual 翻译( 一百零一)
查看>>
GNU make manual 翻译( 一百四十五)
查看>>
【OpenCV学习】cvseqpartition序列分类
查看>>
HttpClient
查看>>
mfc 双冒号
查看>>
MySQL索引简单分析
查看>>
ECSHOP首页调取固定的某个分类
查看>>
用Gvim建立IDE编程环境 (Windows篇)_Nothing is impossible for a willing heart._百度空间...
查看>>
oracle for update和for update nowait的区别
查看>>
poj 1386 Play on Words
查看>>
到了最后出现败笔
查看>>
用VS自带的dotfuscator.exe对exe或dll进行简单加密 以保护程序源码
查看>>
Chrome 插件
查看>>
iptables 1.4.17 发布,Linux防火墙
查看>>
sed 与 awk
查看>>
《Effective C#》读书笔记——条目24:用委托实现回调<使用C#表达设计>
查看>>