• Unity Shader 之 基础纹理


    基础纹理

    纹理的目的就是使用一张图片来控制模型的外观。使用纹理映射(texture mapping)技术,我们可以把一张图“粘”在模型表面,逐纹素(texel)地控制模型的颜色。

    建模软件中利用纹理展开技术把纹理映射坐标(texture-mapping coordinates)存储在每个顶点上。纹理映射坐标(UV坐标,u为横向坐标,v为纵向坐标)定义了该顶点纹理中对应的2D坐标

    UV坐标通常都会被归一化到[0,1]范围内。而Unity中用的坐标为下图,与OpenGL中的坐标相同,而DirectX的坐标原点在左上角。

    单张纹理

    我们通常会用一张纹理代替物体的漫反射颜色。

     1 Shader "Unity My Shader/Diffuse Texture"
     2 {
     3     Properties
     4     {
     5         _Color("Color", Color) = (1,1,1,1)
     6         _MainTex ("Main Tex", 2D) = "white" {}
     7         _Specular ("Specular", Color) = (1,1,1,1)
     8         _Gloss("Gloss", Range(8.0, 256)) = 20
     9     }
    10     SubShader
    11     {
    12         Pass
    13         {
    14             Tags{"LightMode"="ForwardBase"}
    15 
    16             CGPROGRAM
    17             #pragma vertex vert
    18             #pragma fragment frag
    19             
    20             #include "UnityCG.cginc"
    21             #include "Lighting.cginc"
    22 
    23             fixed4 _Color;
    24             sampler2D _MainTex;
    25             float4 _MainTex_ST;
    26             fixed4 _Specular;
    27             float _Gloss;
    28 
    29             struct a2v
    30             {
    31                 float4 vertex : POSITION;
    32                 float3 normal : NORMAL;
    33                 // 将模型的第一组纹理坐标存储到该变量中
    34                 float3 texcoord : TEXCOORD0;
    35             };
    36 
    37             struct v2f
    38             {
    39                 float4 pos : SV_POSITION;
    40                 float3 worldPos : TEXCOORD0;
    41                 float3 worldNormal : TEXCOORD1;
    42                 float2 uv : TEXCOORD2;
    43             };
    44             
    45             v2f vert (a2v v)
    46             {
    47                 v2f o;
    48 
    49                 o.pos = UnityObjectToClipPos(v.vertex);
    50                 // 模型坐标顶点转换世界坐标顶点
    51                 o.worldPos = mul(unity_ObjectToWorld, v.vertex);
    52                 // 模型坐标法线转换世界坐标法线
    53                 o.worldNormal = UnityObjectToWorldNormal(v.normal);
    54                 // 对顶点纹理坐标进行变换,最终得到uv坐标。
    55                 // 方法原理 o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
    56                 //_MainTex_ST 是纹理的属性值,写法是固定的为 纹理名+_ST
    57                 o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    58                 
    59                 return o;
    60             }
    61             
    62             fixed4 frag (v2f i) : SV_Target
    63             {
    64                 // 法线方向
    65                 fixed3 worldNormal = normalize(i.worldNormal);
    66                 // 光照方向
    67                 fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
    68                 // 视角方向
    69                 fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
    70                 // 对纹理进行采样,返回为计算得到的纹素值,与_Color的乘积作为反射率
    71                 fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
    72                 // 环境光
    73                 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
    74                 // 漫反射
    75                 fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
    76                 // Blinn模型 计算
    77                 fixed3 halfDir = normalize(worldViewDir + worldLightDir);
    78                 // 高光反射
    79                 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
    80                 // 相加后输出颜色
    81                 return fixed4(ambient + diffuse + specular, 1);
    82             }
    83             ENDCG
    84         }
    85     }
    86 }

    凹凸映射

    目的:使用一张纹理来修改模型表面的法线,以便为模型提供更多细节。不会真的改变模型的顶点位置,只是让模型看起来好像“凹凸不平”。

    实现凹凸映射的两种方法:高度映射(height mapping)和法线映射(normal mapping)

    • 高度映射:使用高度纹理(height map)模拟表面位移(displacement),然后得到一个修改后的法线值。
    • 法线映射:使用法线纹理(normal map)来直接存储表面法线。

    高度纹理

    高度图:存储强度值(intensity),表示模型表面局部的海拔高度。颜色越浅表明该位置的表明越向外凸起,越深越向内凹陷。

    缺点:计算时不能直接得到表面法线,得同过灰度值计算得到,消耗性能。

    法线纹理

    法线纹理中存储的就是表面的法线方向。由于法线方向的分量范围在[-1,1],而像素的分量范围是[0,1],所以需要映射一下: pixel = (normal + 1) / 2

    反之: normal = pixel * 2 - 1

    效果如下:

    不开启凹凸效果                                                              开启凹凸效果(切线空间)                                                      

      

    开启凹凸效果(世界空间)

    Shader代码如下:

    在切线空间下的凹凸纹理实现

      1 Shader "Unity My Shader/aotu Texture"
      2 {
      3     Properties
      4     {
      5         _Color("Color", Color) = (1,1,1,1)
      6         _MainTex ("Main Tex", 2D) = "white" {}
      7         _BumpTex ("Bump Tex", 2D) = "Bump" {}
      8         _BumpScale ("Bump Scale", Float) = 1
      9         _Specular ("Specular", Color) = (1,1,1,1)
     10         _Gloss("Gloss", Range(8.0, 256)) = 20
     11     }
     12     SubShader
     13     {
     14         Pass
     15         {
     16             Tags{"LightMode"="ForwardBase"}
     17 
     18             CGPROGRAM
     19             #pragma vertex vert
     20             #pragma fragment frag
     21             
     22             #include "UnityCG.cginc"
     23             #include "Lighting.cginc"
     24 
     25             fixed4 _Color;
     26             sampler2D _MainTex;
     27             float4 _MainTex_ST;
     28             sampler2D _BumpTex;
     29             float4 _BumpTex_ST;
     30             float _BumpScale;
     31             fixed4 _Specular;
     32             float _Gloss;
     33 
     34             struct a2v
     35             {
     36                 float4 vertex : POSITION;
     37                 // 将模型的法线方向存储到变量中
     38                 float3 normal : NORMAL;
     39                 // 将模型的第一组纹理坐标存储到变量中
     40                 float3 texcoord : TEXCOORD0;
     41                 // 将模型的顶点切线方向存储到变量中,float4的原因是用w来决定切线空间的第三个坐标轴——福切线的方向性。
     42                 float4 tangent : TANGENT;
     43             };
     44 
     45             struct v2f
     46             {
     47                 float4 pos : SV_POSITION;
     48                 float3 lightDir : TEXCOORD0;
     49                 fixed3 viewDir : TEXCOORD1;
     50                 // 存储两个uv坐标 _MainTex 与 _BumpTex
     51                 float4 uv : TEXCOORD2;
     52             };
     53             
     54             v2f vert (a2v v)
     55             {
     56                 v2f o;
     57 
     58                 o.pos = UnityObjectToClipPos(v.vertex);
     59 
     60                 // 计算福切线方向 叉乘获得与垂直于法线和切线平面的福切线方向,v.tangent.w 用来抉择福切线的方向(因为有两个方向)
     61                 // 方法原理 float3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;
     62                 // 模型空间到切线空间的变换矩阵
     63                 // 方法原理 float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
     64                 TANGENT_SPACE_ROTATION;
     65 
     66                 // ObjSpaceLightDir(模型空间的光照方向),转换成切线空间的光照方向。
     67                 o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
     68                 // 同上,转换的是视角方向
     69                 o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
     70 
     71                 // 对顶点纹理坐标进行变换,最终得到uv坐标。
     72                 // 方法原理 o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
     73                 //_MainTex_ST 是纹理的属性值,写法是固定的为 纹理名+_ST
     74                 o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
     75                 o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpTex);
     76                 
     77                 return o;
     78             }
     79             
     80             fixed4 frag (v2f i) : SV_Target
     81             {
     82                 // 切线空间的光照方向
     83                 fixed3 tangentLightDir = normalize(i.lightDir);
     84                 // 切线空间的视角方向
     85                 fixed3 tangentViewDir = normalize(i.viewDir);
     86 
     87                 // 对法线纹理进行采样
     88                 fixed4 packedNormal = tex2D(_BumpTex, i.uv.zw);
     89                 // 转换映射
     90                 // 方法原理 tangentNormal.xy = (packedNormal.xy * 2 - 1);
     91                 fixed3 tangentNormal =  UnpackNormal(packedNormal);
     92                 tangentNormal.xy *= _BumpScale;
     93                 tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
     94 
     95                 // 对纹理进行采样,返回为计算得到的纹素值,与_Color的乘积作为反射率
     96                 fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
     97                 // 环境光
     98                 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
     99                 // 漫反射
    100                 fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));
    101                 // Blinn模型 计算
    102                 fixed3 halfDir = normalize(tangentViewDir + tangentLightDir);
    103                 // 高光反射
    104                 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss);
    105                 // 相加后输出颜色
    106                 return fixed4(ambient + diffuse + specular, 1);
    107             }
    108             ENDCG
    109         }
    110     }
    111 }

    在世界空间下计算

      1         _BumpTex ("Bump Tex", 2D) = "Bump" {}
      2         _BumpScale ("Bump Scale", Float) = 1
      3         _Specular ("Specular", Color) = (1,1,1,1)
      4         _Gloss("Gloss", Range(8.0, 256)) = 20
      5     }
      6     SubShader
      7     {
      8         Pass
      9         {
     10             Tags{"LightMode"="ForwardBase"}
     11 
     12             CGPROGRAM
     13             #pragma vertex vert
     14             #pragma fragment frag
     15             
     16             #include "UnityCG.cginc"
     17             #include "Lighting.cginc"
     18 
     19             fixed4 _Color;
     20             sampler2D _MainTex;
     21             float4 _MainTex_ST;
     22             sampler2D _BumpTex;
     23             float4 _BumpTex_ST;
     24             float _BumpScale;
     25             fixed4 _Specular;
     26             float _Gloss;
     27 
     28             struct a2v
     29             {
     30                 float4 vertex : POSITION;
     31                 // 将模型的法线方向存储到变量中
     32                 float3 normal : NORMAL;
     33                 // 将模型的第一组纹理坐标存储到变量中
     34                 float3 texcoord : TEXCOORD0;
     35                 // 将模型的顶点切线方向存储到变量中,float4的原因是用w来决定切线空间的第三个坐标轴——福切线的方向性。
     36                 float4 tangent : TANGENT;
     37             };
     38 
     39             struct v2f
     40             {
     41                 float4 pos : SV_POSITION;
     42                 float4 TtoW0 : TEXCOORD0;
     43                 float4 TtoW1 : TEXCOORD1;
     44                 float4 TtoW2 : TEXCOORD2;
     45                 float4 uv : TEXCOORD3;
     46             };
     47             
     48             v2f vert (a2v v)
     49             {
     50                 v2f o;
     51 
     52                 o.pos = UnityObjectToClipPos(v.vertex);
     53 
     54                 // 世界空间顶点坐标
     55                 float3 worldPos = mul(unity_ObjectToWorld, v.vertex);
     56                 // 世界空间法线方向
     57                 float3 worldNormal = UnityObjectToWorldNormal(v.normal);
     58                 // 世界空间切线方向
     59                 float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
     60                 // 世界空间福切线方向
     61                 float3 worldBinormal = cross(worldTangent, worldNormal) * v.tangent.w;
     62 
     63                 // 对顶点纹理坐标进行变换,最终得到uv坐标。
     64                 // 方法原理 o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
     65                 //_MainTex_ST 是纹理的属性值,写法是固定的为 纹理名+_ST
     66                 o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
     67                 o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpTex);
     68 
     69                 // 类矩阵形式保存
     70                 o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
     71                 o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
     72                 o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
     73                 
     74                 return o;
     75             }
     76             
     77             fixed4 frag (v2f i) : SV_Target
     78             {
     79                 // 构建世界空间的顶点坐标
     80                 float3 worldPos = fixed3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
     81                 // 世界空间的光照方向
     82                 fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(worldPos));
     83                 // 世界空间的视角方向
     84                 fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
     85 
     86                 // 对法线纹理进行采样
     87                 fixed4 packedNormal = tex2D(_BumpTex, i.uv.zw);
     88                 // 转换映射
     89                 // 方法原理 tangentNormal.xy = (packedNormal.xy * 2 - 1);
     90                 fixed3 bump =  UnpackNormal(packedNormal);
     91                 bump.xy *= _BumpScale;
     92                 bump.z = sqrt(1.0 - saturate(dot(bump.xy, bump.xy)));
     93                 // 把法线变换到世界空间下(法线原为切线空间),通过使用点乘操作来实现矩阵的每一行和法线相乘得到
     94                 bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
     95 
     96                 // 对纹理进行采样,返回为计算得到的纹素值,与_Color的乘积作为反射率
     97                 fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
     98                 // 环境光
     99                 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
    100                 // 漫反射
    101                 fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(bump, worldLightDir));
    102                 // Blinn模型 计算
    103                 fixed3 halfDir = normalize(worldViewDir + worldLightDir);
    104                 // 高光反射
    105                 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(bump, halfDir)), _Gloss);
    106                 // 相加后输出颜色
    107                 return fixed4(ambient + diffuse + specular, 1);
    108             }
    109             ENDCG
    110         }
    111     }
    112 }

    遮罩纹理

    作用:遮罩允许我们可以保护某些区域,使它们免于某些修改

    流程:采样得到遮罩纹理的纹素值,使用其中摸个(或某几个)通道的值来与某种表面的属性进行相乘,这样,当该通道的值为0时,可以保护表面不受该属性影响

    漫反射+高光反射+凹凸纹理+遮罩

    Shader如下

      1 Shader "Unity My Shader/aotu Texture"
      2 {
      3     Properties
      4     {
      5         _Color("Color", Color) = (1,1,1,1)
      6         _MainTex ("Main Tex", 2D) = "white" {}
      7         _BumpTex ("Bump Tex", 2D) = "Bump" {}
      8         _BumpScale ("Bump Scale", Float) = 1
      9         _SpecularMask ("Specular Mask Tex", 2D) = "white" {}
     10         _SpecularScale ("Specular Scale", Float) = 1
     11         _Specular ("Specular", Color) = (1,1,1,1)
     12         _Gloss("Gloss", Range(8.0, 256)) = 20
     13     }
     14     SubShader
     15     {
     16         Pass
     17         {
     18             Tags{"LightMode"="ForwardBase"}
     19 
     20             CGPROGRAM
     21             #pragma vertex vert
     22             #pragma fragment frag
     23             
     24             #include "UnityCG.cginc"
     25             #include "Lighting.cginc"
     26 
     27             fixed4 _Color;
     28             sampler2D _MainTex;
     29             float4 _MainTex_ST;
     30             sampler2D _BumpTex;
     31             float4 _BumpTex_ST;
     32             float _BumpScale;
     33             sampler2D _SpecularMask;
     34             float4 _SpecularMask_ST;
     35             float _SpecularScale;
     36             fixed4 _Specular;
     37             float _Gloss;
     38 
     39             struct a2v
     40             {
     41                 float4 vertex : POSITION;
     42                 // 将模型的法线方向存储到变量中
     43                 float3 normal : NORMAL;
     44                 // 将模型的第一组纹理坐标存储到变量中
     45                 float3 texcoord : TEXCOORD0;
     46                 // 将模型的顶点切线方向存储到变量中,float4的原因是用w来决定切线空间的第三个坐标轴——福切线的方向性。
     47                 float4 tangent : TANGENT;
     48             };
     49 
     50             struct v2f
     51             {
     52                 float4 pos : SV_POSITION;
     53                 float2 uv : TEXCOORD0;
     54                 float3 lightDir : TEXCOORD1;
     55                 float3 viewDir : TEXCOORD2;
     56             };
     57             
     58             v2f vert (a2v v)
     59             {
     60                 v2f o;
     61 
     62                 o.pos = UnityObjectToClipPos(v.vertex);
     63 
     64                 o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
     65 
     66                 TANGENT_SPACE_ROTATION;
     67 
     68                 o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
     69                 o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));
     70                 
     71                 return o;
     72             }
     73             
     74             fixed4 frag (v2f i) : SV_Target
     75             {
     76                 // 世界空间的光照方向
     77                 fixed3 tangentLightDir = normalize(i.lightDir);
     78                 // 世界空间的视角方向
     79                 fixed3 tangentViewDir = normalize(i.viewDir);
     80 
     81                 // 对法线纹理进行采样
     82                 fixed4 packedNormal = tex2D(_BumpTex, i.uv);
     83                 fixed3 tangentNormal =  UnpackNormal(packedNormal);
     84                 tangentNormal.xy *= _BumpScale;
     85                 tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
     86 
     87                 // 对纹理进行采样,返回为计算得到的纹素值,与_Color的乘积作为反射率
     88                 fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
     89                 // 环境光
     90                 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
     91                 // 漫反射
     92                 fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));
     93                 // Blinn模型 计算
     94                 fixed3 halfDir = normalize(tangentViewDir + tangentLightDir);
     95                 // 对高光反射的r通道计算掩码值
     96                 fixed specularMask = tex2D(_SpecularMask, i.uv).r * _SpecularScale;
     97                 // 高光反射 添加遮罩
     98                 fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss) * specularMask;
     99                 // 相加后输出颜色
    100                 return fixed4(ambient + diffuse + specular, 1);
    101             }
    102             ENDCG
    103         }
    104     }
    105 }

    (完)

  • 相关阅读:
    关于post和get的区别
    修改ubuntu系统时区
    修改 Ubuntu 下 Mysql 编码
    C++程序设计实践指导1.10二维数组元素换位改写要求实现
    C++程序设计实践指导1.7超长数列中n个数排序改写要求实现
    C++程序设计实践指导1.15找出回文数改写要求实现
    C++程序设计实践指导1.14字符串交叉插入改写要求实现
    C++程序设计实践指导1.13自然数集中找合数改写要求实现
    C++程序设计实践指导1.12数组中数据线性变换改写要求实现
    C++程序设计实践指导1.9统计与替换字符串中的关键字改写要求实现
  • 原文地址:https://www.cnblogs.com/SHOR/p/7927654.html
Copyright © 2020-2023  润新知