【Unity Shaders】Reflecting Your World —— Unity3D中的法线贴图和反射

http://blog.csdn.net/candycat1992/article/details/24541623

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

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

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

写在前面

有很多情况你可能想要使用法线去影响反射效果。比如,你想要模拟一个被霜雪覆盖的玻璃材质的表面,或者一个冰块。如果你根据物理知识真实的模拟这个平面的每一个细节部分,那么你就不要希望你游戏的FPS还可以达到60帧了。相反的,我们可以使用法线贴图来伪造一个视觉体验,因此我们需要学习如何将法线贴图的信息传递给反射效果。

为了完成这个任务,我们将要学习Input结构体的另一个内置参数,它可以将由法线贴图技术生成的修改后的平面法线信息传递进来。下面,让我们来具体看一下如何修改Input结构体来达到这个效果吧!

准备

  1. 首先,我们需要一个Cubemap来产生反射效果。所以你可以使用前一节中的Cubemap,或者生成一个新的。这一节中,我们使用的如下所示(你可以在本书资源中找到它):
  2. 我们还需要一张法线贴图来产生基于法线的反射效果。
  3. 最后,创建一个新的场景、一个球体、一个平面以及一个平行光。同时,还需要创建一个新的Shader和Material,命名为NormalMappedReflection。

实现

  1. 首先让我们添加新的properties,使得我们能够添加自己的Cubemap和法线贴图。这个步骤你应该非常熟悉了。向Properties块添加下列代码:

    [plain] view plain copy

    print?

    1. Properties {
    2. _MainTint ("Diffuse Tint", Color) = (1,1,1,1)
    3. _MainTex ("Base (RGB)", 2D) = "white" {}
    4. _NormalMap ("Normal Map", 2D) = "bump" {}
    5. _Cubemap ("Cubemap", CUBE) = ""{}
    6. _ReflAmount ("Reflection Amount", Range(0,1)) = 0.5
    7. }
  2. 然后,我们需要在SubShader块声明这些properties,使得我们能够访问Properties块中的这些数据:

    [plain] view plain copy

    print?

    1. CGPROGRAM
    2. #pragma surface surf Lambert
    3. samplerCUBE _Cubemap;
    4. sampler2D _MainTex;
    5. sampler2D _NormalMap;
    6. float4 _MainTint;
    7. float _ReflAmount;
  3. 然后,修改Input结构体。这是基于法线贴图的反射的精华所在。通过使用INTERNAL_DATA声明,我们可以访问经过法线贴图修改后的平面的法线信息:

    [plain] view plain copy

    print?

    1. struct Input {
    2. float2 uv_MainTex;
    3. float2 uv_NormalMap;
    4. float3 worldRefl;
    5. INTERNAL_DATA
    6. };
  4. 最后,我们需要修改surf函数,来得到最后的基于法线贴图的反射效果:

    [plain] view plain copy

    print?

    1. void surf (Input IN, inout SurfaceOutput o) {
    2. half4 c = tex2D (_MainTex, IN.uv_MainTex);
    3. float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap)).rgb;
    4. o.Normal = normals;
    5. o.Emission = texCUBE (_Cubemap, WorldReflectionVector (IN, o.Normal)).rgb * _ReflAmount;
    6. o.Albedo = c.rgb * _MainTint;
    7. o.Alpha = c.a;
    8. }

最后,整体代码如下:

[plain] view plain copy

print?

  1. Shader "Custom/NormalMappedReflection" {
  2. Properties {
  3. _MainTint ("Diffuse Tint", Color) = (1,1,1,1)
  4. _MainTex ("Base (RGB)", 2D) = "white" {}
  5. _NormalMap ("Normal Map", 2D) = "bump" {}
  6. _Cubemap ("Cubemap", CUBE) = ""{}
  7. _ReflAmount ("Reflection Amount", Range(0,1)) = 0.5
  8. }
  9. SubShader {
  10. Tags { "RenderType"="Opaque" }
  11. LOD 200
  12. CGPROGRAM
  13. #pragma surface surf Lambert
  14. samplerCUBE _Cubemap;
  15. sampler2D _MainTex;
  16. sampler2D _NormalMap;
  17. float4 _MainTint;
  18. float _ReflAmount;
  19. struct Input {
  20. float2 uv_MainTex;
  21. float2 uv_NormalMap;
  22. float3 worldRefl;
  23. INTERNAL_DATA
  24. };
  25. void surf (Input IN, inout SurfaceOutput o) {
  26. half4 c = tex2D (_MainTex, IN.uv_MainTex);
  27. float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap)).rgb;
  28. o.Normal = normals;
  29. o.Emission = texCUBE (_Cubemap, WorldReflectionVector (IN, o.Normal)).rgb * _ReflAmount;
  30. o.Albedo = c.rgb * _MainTint;
  31. o.Alpha = c.a;
  32. }
  33. ENDCG
  34. }
  35. FallBack "Diffuse"
  36. }

效果如下(一个表明凹凸不平的反射球):

而下图是不加法线影响的效果(一个光滑的反射球):

解释

你可能已经注意到这一节的Shader和上一节中的非常类似,而有一个非常重要的不同点(上一节直接使用IN.worldRefl来查找Cubemap)。我们想要使用逐像素的法线贴图来修改我们的反射贴图。为了完成这个目的,我们需要得到在应用法线贴图后、物体的平面法线信息。这意味着,我们需要下列代码:

[plain] view plain copy

print?

  1. float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap)).rgb;
  2. o.Normal = normals;

一旦上述代码在Shader中执行后,物体的平面法线将被修改;因此,我们需要使用它来影响我们的反射。我们可以通过声明INTERNAL_DATA来访问修改后的法线信息,然后使用WorldReflectionVector (IN, o.Normal)去查找Cubemap中对应的反射信息。这是Unity提供给我们的另一个内置函数,因此我们不需要再自己写那些冗长的代码,而仅仅需要关注编写Shader中产生关键效果的部分。

更多…

结构体中还有很多内置函数(变量),而在将来的章节中我们的确会接触到它们。下面的表格描述了每一个内置函数的作用以及如何使用它们。你也可以访问官网来得到更多的关于内置函数的信息:

float3 viewDir 包含了视角的观察方向,主要用于计算视差效应(Parallax effects ),边缘光照,等等。
float4 COLOR 包含了经过内插值(interpolated )的每个顶点的颜色值。
float4 screenPos 包含了用于反射效果的屏幕坐标系的位置信息。例如,在Unity专业版的WetStreet shader中使用了它。
float3 worldPos 包含了世界坐标系中的位置。
float3 worldRefl 包含了世界坐标系中的反射向量,如果Surface Shader没有重写o.Normal。参考Reflect-Diffuse shader。
float3 worldNormal 包含了世界坐标系中的法线向量,如果Surface Shader没有重写o.Normal。
float3 worldRefl;
INTERNAL_DATA
包含了世界坐标系的反射向量,如果Surface Shader重写了o.Normal。为了得到基于逐像素的法线贴图的反射向量,请使用WorldReflectionVector (IN,o.Normal) 。参考Reflect-Bumped shader。
float3 worldNormal;
INTERNAL_DATA
包含了世界坐标系的发现向量,如果Surface Shader重写了o.Normal。为了得到基于逐像素的法线贴图的法线向量,请使用WorldNormalVector (IN,o.Normal) 。参考Reflect-Bumped shader。
时间: 2024-12-23 06:01:14

【Unity Shaders】Reflecting Your World —— Unity3D中的法线贴图和反射的相关文章

Unity Shaders and Effects Cookbook (4-4)在Cubemap 上使用 法线贴图 (法线贴图与反射)

法线贴图 在之前学习过了,我们使用法线贴图在低分辨率的模型上 模拟 高分辨率的效果. Unity中 通过 UnpackNormal 函数 来使用法线贴图. 之前学习法线贴图的记录 Unity Shaders and Effects Cookbook (2-5) 如何使用法线贴图 这一节讲的是 在Cubemap 上使用法线贴图.模拟凹凸效果. 最终效果如图 一起来做吧. 首先搭建好场景,和上一节一样. 导入法线贴图 创建材质 .Shader . 复制上一节的 Shader 就行.然后修改成下面的内

Unity Shaders and Effects Cookbook (4-1)(4-2)静态立方体贴图的创建与使用

开始学习第4章 - 着色器的反射 看完了1.2节,来记录一下.反射主要是利用了 Cubemap 立方体贴图. 认识Cubemap 立方体贴图,就如同名字所说,在一个立方体上有6张图,就这样认为吧. 假想一下 ,在一个艳丽的房间里,有一个表面是镜子的圆球,那这个圆球表面就反射了房间里面的所有东西,就是一个大号的凸镜. 这是到网上找得一张图,很直观的表达了我的意思-- 注意标题中说的,静态立方体贴图,为什么叫静态,因为这一次使用的立方体贴图是提前生成好的图片,而不是动态生成的. 这又是什么意思呢?

Unity Shaders and Effects Cookbook (4-6)震撼的实时反射 动态立方图系统

昨天逛街的时候看到太平鸟里面摆了个金属的米老鼠,于是职业病犯了,一直在想金属的颜色是什么,这个反射该怎么写,想不出来-- 今天正好看到动态反射立方图系统这一节,看完觉得很别扭,因为书上介绍的是事先踩点生成Cubemap的方式而不是实时的.于是到官方文档找到实时反射的代码,做了一个比较花俏的场景,运行之后吃了一大惊,实时反射是如此的震撼.. 第四章第一节第二节介绍了创建Cubemap,然后学习了如何使用. Unity Shaders and Effects Cookbook (4-1)(4-2)静

Unity3D ShaderLab法线贴图

Unity3D ShaderLab法线贴图 说到法线贴图,应该算是我们最常使用的一种增强视觉效果的贴图.将法线贴图的各个像素点座位模型的法线,这样我们的光照可以模拟出高分辨率的效果, 同时也保持较低的多边形数.法线贴图通常存储在一个普通的rgb图片,他的rgb分量分别对应了曲面法线的xyz坐标. 在Unity中,会通过UnpackNormals()函数来使用法线贴图,这使得在表面着色范围内为我们的着色器添加使用法线的过程变得更容易. 首先,创建一个shader和材质球.我们开始修改shader代

【Unity Shaders】Reflecting Your World —— 在Unity3D中创建一个简单的动态Cubemap系统

本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源(当然你也可以从官网下载). ========================================== 分割线 ========================================== 写在前面 我们已经学了很多关于反射的内容,但是我们现在的反射并不能实时反射,即当反射物体移动时它们不

【Unity Shaders】Shader中的光照

写在前面 自己写过Vertex & Fragment Shader的童鞋,大概都会对Unity的光照痛恨不已.当然,我相信这是因为我们写得少...不过这也是由于官方文档对这方面介绍很少的缘故,导致我们无法自如地处理很多常见的光照变量.这篇我们就来讨论下Unity内置的一些光照变量和函数到底怎么用. 以下内容均建立在Forward Rendering Path的基础上. 自己总结的,如果有硬伤一定要告诉我啊!感激不尽~ 主要参考: http://en.wikibooks.org/wiki/Cg_P

【Unity Shaders】使用Unity Render Textures实现画面特效——画面特效中的叠加(Overlay)混合模式

本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源(当然你也可以从官网下载). ========================================== 分割线 ========================================== 写在前面 在这篇里,我们将会学习另一种混合模式,叠加(Overlay)混合模式.这种混合模式使用了条

Unity Shaders and Effects Cookbook (7-1) 在Surface Shader 中 访问 顶点颜色

在OpenGL中,需要顶点着色器和片段着色器的支持才能进行正确的渲染,在顶点着色器中,每一帧 对 场景中物体的每个顶点都要执行一次处理. 如果自己使用OpenGL,需要在C++ 代码读取模型数据,将顶点坐标.顶点颜色.UV坐标传递到顶点着色器中. 所以在顶点着色器中 ,是可以对顶点数据进行修改的. 搭建好测试场景,导入随书的FBX模型. 在Unity3d中,将FBX 或者其它模型  导入编辑器,默认会创建一个 Diffuse 材质.这个材质中是 没有顶点函数的,也就是说不能 对 顶点数据进行处理

Unity Shaders and Effects Cookbook (7-2) Surface Shader 中实现 顶点动画

上一节中说了,在 Surface Shader 中,添加顶点函数,我们可以在 顶点函数中获取到 顶点数据,比如顶点颜色.顶点坐标等. 这一节学习获取顶点坐标,并且修改顶点坐标,来实现顶点动画. 简单介绍原理: 在顶点函数中,获取到顶点坐标 vertex,然后,求float offsetY = sin(vertex.x) ,然后将 offsetY 加到 vertex.y 上,这样就把原来的平面 ,变成了 正弦 波浪. 然后再使用之前学过的 内置变量 _Time ,算式变为 float offset