zoukankan      html  css  js  c++  java
  • 【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.   
      4. samplerCUBE _Cubemap;  
      5. sampler2D _MainTex;  
      6. sampler2D _NormalMap;  
      7. float4 _MainTint;  
      8. 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.       
      4.     float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap)).rgb;  
      5.     o.Normal = normals;  
      6.       
      7.     o.Emission = texCUBE (_Cubemap, WorldReflectionVector (IN, o.Normal)).rgb * _ReflAmount;  
      8.     o.Albedo = c.rgb * _MainTint;  
      9.     o.Alpha = c.a;  
      10. }  
     
    最后,整体代码如下:
    [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.           
    13.         CGPROGRAM  
    14.         #pragma surface surf Lambert  
    15.   
    16.         samplerCUBE _Cubemap;  
    17.         sampler2D _MainTex;  
    18.         sampler2D _NormalMap;  
    19.         float4 _MainTint;  
    20.         float _ReflAmount;  
    21.   
    22.         struct Input {  
    23.             float2 uv_MainTex;  
    24.             float2 uv_NormalMap;  
    25.             float3 worldRefl;  
    26.             INTERNAL_DATA  
    27.         };  
    28.   
    29.         void surf (Input IN, inout SurfaceOutput o) {  
    30.             half4 c = tex2D (_MainTex, IN.uv_MainTex);  
    31.               
    32.             float3 normals = UnpackNormal(tex2D(_NormalMap, IN.uv_NormalMap)).rgb;  
    33.             o.Normal = normals;  
    34.               
    35.             o.Emission = texCUBE (_Cubemap, WorldReflectionVector (IN, o.Normal)).rgb * _ReflAmount;  
    36.             o.Albedo = c.rgb * _MainTint;  
    37.             o.Alpha = c.a;  
    38.         }  
    39.         ENDCG  
    40.     }   
    41.     FallBack "Diffuse"  
    42. }  

    效果如下(一个表明凹凸不平的反射球):
    而下图是不加法线影响的效果(一个光滑的反射球):
     
     
     

    解释

     
    你可能已经注意到这一节的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。
  • 相关阅读:
    全面监测网站信息
    linux 将Mysql的一张表导出至Excel格式文件
    渗透测试人员发现用户可无限输入密码次数,超过5次未锁定用户,存在暴力破解风险。解放方案:限制每个输入的用户名(不管存不存在该账户)登陆失败次数不超过5次,超过则锁定该用户
    mysql linux下数据库导出 常用操作
    find php.ini 和 php的执行目录 bin目录
    解决:The “https://packagist.laravel-china.org/packages.json” file could not be downloaded
    如何上传代码至GitHub
    7. Jmeter-逻辑控制器介绍与使用
    19、Linux命令对服务器内存进行监控
    20、Linux命令对服务器磁盘进行监控
  • 原文地址:https://www.cnblogs.com/alps/p/7108731.html
Copyright © 2011-2022 走看看