通过图片控制模型外观,使用纹理映射(texture mapping)。通过纹理展开技术把纹理映射(texture mapping coordinates)坐标存储到每个顶点上。纹理映射坐标定义了该顶点在纹理中对应的2D坐标。UV(u,v),u表示横坐标,v表示纵坐标。OpenGL的原点左下角,DirectX的原点在左上角。
属性: | 功能: |
---|---|
Texture Type | 使用此属性可定义纹理的用途。纹理导入器中的其他属性将根据此处的选择而变化。 |
Default | 这是用于所有纹理的最常用设置。此选项可用于访问大多数纹理导入属性。 |
Normal Map | 选择此选项可将颜色通道转换为适合实时法线贴图的格式。请参阅导入纹理以了解有关法线贴图的更多信息。 |
Editor GUI | 如果要在任何 HUD 或 GUI 控件上使用纹理,请选择此选项。 |
Sprite (2D and UI) | 如果要在 2D 游戏中使用该纹理作为精灵,请选择此选项。 |
Cursor | 如果要将纹理用作自定义游标,请选择此选项。 |
Cookie | 选择此选项可通过基本参数来设置纹理,从而将其用于场景光源的剪影。 |
Lightmap | Select this if you are using the Texture as a Lightmap. This option enables encoding into a specific format (RGBM or dLDR, depending on the platform) and a post-processing step on Texture data (a push-pull dilation pass). |
Single Channel | 如果在纹理中只需要一个通道,请选择此选项。 |
纹理导入器中的第二个属性是 Texture Shape。使用此属性可选择和定义纹理的形状和结构。
属性: | 功能: |
---|---|
Texture Shape | 使用此属性可定义纹理的形状。默认情况下,该值设置为 2D。 |
2D | 这是用于所有纹理的最常用设置;它将图像文件定义为 2D 纹理。这些设置用于将纹理映射到 3D 网格和 GUI 元素以及其他项目元素。 |
Cube | 此选项将纹理定义为立方体贴图。例如,可将其用于天空盒或反射探针。选择 Cube 会显示不同的贴图选项。 |
Mapping | 仅当 Texture Shape 设置为 Cube 时,此设置才可用。使用 Mapping 可指定如何将纹理投影到游戏对象上。默认情况下,此设置为 Auto。 |
Auto | Unity 尝试从纹理信息自动计算布局。 |
6 Frames Layout (Cubic Environment) | 纹理包含按以下标准立方体贴图布局之一排列的六个图像:交叉或序列 (+x -x +y -y +z -z)。图像可以水平或垂直定向。 |
Latitude Longitude (Cylindrical) | 将纹理映射到 2D 纬度/经度表示形式。 |
Mirrored Ball (Sphere Mapped) | 将纹理映射到类似球体的立方体贴图。 |
Convolution Type | 选择要用于此纹理的预卷积(即过滤)类型。预卷积的结果将存储在 Mip 中。默认情况下,此设置为 None。 |
None | 纹理没有预卷积(无过滤)。 |
Specular (Glossy Reflection) | 选择此选项可将立方体贴图用作反射探针。纹理 Mipmap 使用引擎 BRDF 进行预卷积(过滤)。(请参阅 Wikipedia 的双向反射分布函数 (Bidirectional reflectance distribution function) 页面以了解更多信息。) |
Diffuse (Irradiance) | 对纹理进行卷积(过滤)以表示辐照度。如果将立方体贴图用作光照探针,则此选项非常有用。 |
Fixup Edge Seams | 仅在选择 None 或 Diffuse 卷积(过滤)的情况下,此选项才可用。在低端平台上使用此选项作为解决过滤限制(例如在面之间错误过滤的立方体贴图)的解决方法。 |
Advanced settings
“高级”设置使您可以对Unity处理纹理的方式进行更好的调整。 这些设置的顺序和可用性可能会略有不同,具体取决于您选择的``纹理类型''。
Property: | Description: |
---|---|
Non Power of 2 | 如果纹理的非2幂(NPOT)尺寸大小,则这将定义导入时的缩放行为。 有关两种尺寸的非幂次的更多信息,请参见关于Importing Textures的文档。 默认情况下设置为``无''。 |
None | 纹理尺寸大小保持不变。 |
To nearest | 导入时,“纹理”将缩放为最接近的二维幂数。 例如,将257x511像素的纹理缩放为256x512像素。 注意PVRTC 格式要求“纹理”为正方形(即宽度等于高度),因此最终的尺寸尺寸会放大到512x512像素。 |
To larger | 在导入时,Texture缩放为最大尺寸尺寸值的2幂次方尺寸。 例如,将257x511像素的纹理缩放为512x512像素。 |
To smaller | 在导入时,Texture缩放为最小尺寸尺寸值的2幂次方尺寸。 例如,将257x511像素的纹理缩放为256x256像素。 |
Read/Write Enabled | 启用此选项可从脚本访问纹理数据 使用Texture2D.SetPixels,Texture2D.GetPixels和其他Texture2D方法。 在内部,Unity使用Texture数据的副本进行脚本访问,这使Texture所需的内存量增加了一倍。 因此,默认情况下禁用此属性,并且仅在需要脚本访问权限时才应启用它。 有关更多信息,请参见Text2D。 |
Streaming Mip Maps | 启用此复选框以在此纹理上使用` Texture Streaming ''。 此设置对于Unity用3D显示的3D环境中的任何纹理均有效 渲染器。 漫反射纹理,法线贴图和光照贴图对于纹理流均有效。 |
Mip Map Priority | 使用此设置mipmap的优先级。 Unity使用它来确定分配资源时要优先处理的mipmap。 较高的值表示较高的优先级(例如,3表示优先级高于1)。 仅在启用``流Mip映射''时此设置才可用。 Mip Map Priority编号也是内存预算的mipmap偏移量(启用纹理流时在` Quality Settings '中设置)。 例如,优先级为2,纹理流系统尝试使用比纹理优先级为0的纹理高两个mip级别的mipmap。每个轴更高的一个mip级别是2x,每个轴更高的两个mip级别是4x,因此 两个mip级别越高,纹理越大16倍。 如果无法执行此操作,则使用较低的MIP级别以适应“内存预算”。 负值也有效。 有关更多详细信息,请参见Texture Streaming API。 |
Generate Mip Maps | 选中此框以启用Mipmap生成。 Mipmap是Texture的较小版本,当Texture在屏幕上很小时会使用。 有关mipmap的更多信息,请参见有关导入纹理的文档。 |
Border Mip Maps | 选中此框可避免颜色渗入较低MIP级别的边缘。 用于light cookies(请参阅下文)。 默认情况下未选中此框。 |
Mip Map Filtering | 有两种方法可以使用mipmap过滤来优化图像质量。 The default option is Box. |
Box | 这是淡出mipmap的最简单方法。 MIP级别随着尺寸尺寸的减小而变得更加平滑。 |
Kaiser | 当mipmap的尺寸减小时,将对它们进行锐化算法。 如果您的纹理距离太模糊,请尝试此选项。 (The algorothm is of a Kaiser Window type - see Wikipedia for further information.) |
Mip Maps Preserve Coverage | 如果希望生成的Mipmap的Alpha通道在Alpha测试期间保留覆盖率,请启用此复选框。See TextureImporterSettings.mipMapsPreserveCoverage for more information. |
Fadeout Mip Maps | 启用此选项后,随着MIP级别的提高,mipmap会逐渐变灰。 这用于局部贴图。 最左边的滚动条是开始淡出的第一个MIP级别。 最右边的滚动条定义纹理完全变灰的MIP级别。 |
Wrap Mode | 选择平铺时纹理的行为。 The default option is Clamp. |
Repeat | 在图块中重复纹理。 |
Clamp | 拉伸纹理的边缘。 |
Mirror | 在每个整数边界处镜像纹理以创建重复图案。 |
Mirror Once | 镜像一次纹理,然后将其钳制到边缘像素 |
Per-axis | 选择此选项可单独控制Unity如何在U轴和V轴上包装纹理。 |
Filter Mode | 选择当纹理通过3D变换拉伸时如何过滤. The default option is Point (no filter). |
Point (no filter) | 最邻近滤波,放大或缩小时,采样像素数目通常只有一个。像素风格 |
Bilinear | 线性滤波,对每个目标像素会找邻居的4个像素,进行插值混合后得到最终的像素。看起来被模糊了。 |
Trilinear | 与线性滤波类似,如果一张纹理没有使用多级渐远技术,效果和Bilinear一样。 |
Aniso Level | 当以陡峭角度查看纹理时,可以提高纹理质量。 适用于地面和地面纹理。See documentation on Importing Textures for more information on Anisotropic filtering. |
特定于平台的覆盖 (Platform-specific overrides)
纹理检视面板窗口有一个__特定于平台的覆盖 (Platform-specific overrides)__ 面板。
特定于平台的覆盖 (Platform-specific overrides) 面板
在为不同平台进行构建时,您需要考虑每个目标平台的分辨率、文件大小与相关内存大小要求、像素尺寸和纹理质量。可使用__特定于平台的覆盖 (Platform-specific overrides)__ 面板设置默认选项(使用 __Default__),然后使用面板顶部的按钮针对特定平台覆盖这些选项的设置。
属性: | 功能: |
---|---|
Max Size | 导入的纹理的最大尺寸(以像素为单位)。美术师通常喜欢使用尺寸较大的纹理;使用 Max Size 可将纹理缩小到合适的尺寸大小。 |
Compression | 选择纹理的压缩类型。此参数有助于系统为纹理选择正确的压缩格式。根据平台和压缩格式的可用性,不同的设置最终可能采用相同的内部格式(例如,__Low Quality Compression__ 对移动平台有影响,但对桌面平台无影响)。 |
None | 不压缩纹理。 |
Low Quality | 以低质量格式压缩纹理。与 Normal Quality 相比,这会降低内存使用量。 |
Normal Quality | 以标准格式压缩纹理。 |
High Quality | 以高质量格式压缩纹理。与 Normal Quality 相比,这会提高内存使用量。 |
Format | 这会绕过自动系统来指定用于纹理的内部表示。可用格式的列表取决于平台和纹理类型。请参阅特定于平台的覆盖的纹理格式相关文档以了解更多信息。 注意:即使未覆盖平台,此选项也会显示自动系统选择的格式。Format 属性仅在覆盖特定平台时可用,而不是作为默认设置。 |
Use crunch compression | 如果适用,使用 Crunch 压缩。Crunch 是一种基于 DXT 或 ETC 纹理压缩的有损压缩格式。在运行时,纹理在 CPU 上解压缩为 DXT 或 ETC,然后上传到 GPU 上。Crunch 压缩有助于纹理在磁盘上使用尽可能少的空间并方便下载。Crunch 纹理可能需要很长时间进行压缩,但在运行时的解压缩速度非常快。 |
Compressor Quality | 使用 Crunch 纹理压缩时,可使用滑动条调整质量。压缩质量越高意味着纹理越大,压缩时间越长。 |
纹理缩放处理比放大更加复杂,需要将多个像素合并为一个像素。其中需要处理抗锯齿的问题,常用解决方法使用多级渐远纹理(mipmapping)。使用多级渐远技术将原纹理提前用虑波处理来得到很多更小的图像,形成图像金字塔,每一层都是对上一层图像降采样的结果。能快速得到结果像素,但是会多出33%的内存空间,空间换时间。
凹凸映射:目的是使用一张纹理来修改模型表面的法线,以便为模型提供更多的细节,不会改变模型的顶点位置。只是让模型看起来像凹凸不平。
两种主要的方法可以用来进行凹凸映射:1.使用高度贴图(height mapping)(高度映射)来模拟表面位移,然后得到一个修改后的法线值。2.直接使用法线纹理(normal mapping)(法线映射)来存储表面法线。
高度纹理:
高度图中存储的是强度值(intensity),表示模型表面局部的海拔高度。颜色越浅表明该位置的表面越向外凸起。越深越向里凹。更加直观,但计算更加复杂。
法线纹理:
法线纹理存储的是表面的法线方向。法线方向的分量范围在[-1,1],而像素的分量范围为[0,1],需要做一个映射。,所以在shader采样后还需要进行一次映射。得到原先的法线方向,
,存储方式:1.直接将修改后的模型空间中的表面法线存储在一张纹理中,模型空间的法线纹理(object-space-normal map)。然而在实际中会使用模型顶点的切线空间(tangent space)来存储法线。
如何计算光照模型中统一各个方向矢量所在的坐标空间。由于法线纹理存储的法线是切线空间下的方向,两种选择:1.选择在切线空间下进行光照计算,此时需要把光照方向、视角方向变换到切线空间;2.在世界空间进行,需要将采样得到的法线方向变换到世界空间下,再和世界空间下的光照方向和视角方向进行计算。第一种方式可以直接再顶点空间完成对光照方向和视角方向的转换,第二种方法需要对法线纹理进行采样,所以变化过程必须到片元着色器中执行,需要一次矩阵操作。效率第一种更高,通用性第二种。
渐变纹理
通过渐变纹理控制漫反射光照的结果。使用半兰伯特光照模型。
遮罩纹理
遮罩纹理可以使设定的区域不被修改。可以更加精确的控制模型表面的各种属性以及混合纹理。一般流程:通过采样得到遮罩纹理的纹素值来与某种表面的属性进行相乘,这样当该通道值为0时,可以保护表面不受该属性的影响。不仅经保护某些区域免于修改,而且可以存储任何我们希望逐像素控制的表面属性。充分利用纹理的RGBA四个通道,用于存储不同的属性。列如,高光反射强度存在R通道,边缘光的强度存在G,高光反射系数存在B,自发光强度存在A;
基础纹理
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unlit/brike"
{
Properties{
_Color("Color",Color)=(1,1,1,1)
_Specular("Spceular",Color)=(1,1,1,1)
_MainTex("Texture",2D)="White"{}
_Gloss("Gloss",Range(8.0,256))=20
}
Subshader{
Pass{
Tags {"LightMode"="ForwardBase"} //定义光照流水线中的角色
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "UnityCG.cginc"
fixed4 _Color;
sampler2D _MainTex; //sampler2D 代表2d纹理
float4 _MainTex_ST;//得到纹理属性时,需要使用 _纹理名_ST 来声明属性,可以得到该纹理的缩放和平移,_ST.xy,存储缩放值,_ST.zw存储偏移值值
fixed4 _Specular;
float _Gloss;
struct a2f{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 texc:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float2 uv:TEXCOORD2;
};
v2f vert(a2f a){
v2f v;
v.pos=UnityObjectToClipPos(a.vertex);
v.worldNormal=normalize(UnityObjectToWorldNormal(a.normal));
v.worldPos=mul(unity_ObjectToWorld,a.vertex).xyz;
//uv 计算公式 纹理的坐标乘以缩放 + 偏移量
v.uv=a.texc.xy * _MainTex_ST.xy + _MainTex_ST.zw;
//通过内置的TRANSFORM_TEX宏定义进行转换。
//v.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
return v;
}
fixed4 frag(v2f v):SV_Target{
fixed3 worldNormal=v.worldNormal;
fixed3 worldLightDir=normalize(UnityWorldSpaceLightDir(v.worldPos));
//通过纹理计算 albedo tex2D函数进行纹理采样 返回纹素值
fixed3 albedo=tex2D(_MainTex,v.uv).rgb * _Color.rgb;
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
//计算diffuse
fixed3 diffuse=_LightColor0.rgb * albedo * saturate(dot(worldNormal,worldLightDir));
//半兰伯特diffuse模型
/*fixed3 halfLambert=0.5*dot(v.worldNormal,worldLightDir)+0.5;
fixed3 diffuse=_LightColor0.rgb * albedo * halfLambert;*/
//得到世界视角方向
fixed3 viewDir=normalize(UnityWorldSpaceViewDir(v.worldPos));
//fixed3 reflectDir=normalize(reflect(-worldLightDir,worldNormal));
//Blinn Phong光照模型 specular 亮点更大一些
fixed3 halfDir=normalize(worldLightDir+viewDir);
fixed3 specular=_LightColor0.rgb * albedo * pow(saturate(dot(worldNormal,halfDir)),_Gloss);
return fixed4(ambient+diffuse+specular,1.0);
}
ENDCG
}
}
FALLBACK "Specular"
}
基础纹理+凹凸纹理
//再切线空间计算
Shader "Unlit/brike_2"
{
Properties{
_Color("Color",Color)=(1,1,1,1)
_Specular("Specular",Color)=(1,1,1,1)
_Gloss("Gloss",Range(8.0,256))=20
_MainTex("MainTex",2D)="White"{}
_BumpMap("BumpMap",2D)="bump"{}//bump 是内置的法线纹理,没有使用任何法线纹理时,就对应了模型自带的法线信息。
_BumpScale("BumpScale",Float)=1.0
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
fixed4 _Specular;
float _Gloss;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _BumpScale;
struct a2f{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 tangent:TANGENT;//通过TANGENT语义存储切线方向。为啥时float4 ? 因为需要使用tangent.w 分量来决定切线空间中的第三个坐标轴----副切线的方向性。
float4 texc:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float4 uv:TEXCOORD0;
float3 lightDir:TEXCOORD1;
float3 viewDir:TEXCOORD2;
};
v2f vert(a2f a){
v2f v;
v.pos=UnityObjectToClipPos(a.vertex);
v.uv.xy=a.texc.xy * _MainTex_ST.xy + _MainTex_ST.zw;
v.uv.zw=a.texc.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
//计算 叉积 法线 和 切线 得到 副切线。 binormal 叉积不满足交换律,满足反交换律。点积满足。
float3 binormal=cross( normalize(a.normal),normalize(a.tangent.xyz)) * a.tangent.w;//(叉积后得到两个方向,乘tangent.w 决定方向)
//模型空间下的切线方向、副切线方向、法线方向 排列组成得到从模型空间到切线空间的变换矩阵。 切线空间。
float3x3 rotation=float3x3(a.tangent.xyz,binormal,a.normal);
//使用内置函数进行转换
//TANGENT_SPACE_ROTATION;
//得到切线空间后,计算出切线空间下的光照方向和视角方向
v.lightDir=mul(rotation,ObjSpaceLightDir(a.vertex)).xyz;
v.viewDir=mul(rotation,ObjSpaceViewDir(a.vertex)).xyz;
return v;
}
fixed4 frag(v2f v):SV_Target{
fixed3 tangentLightDir = normalize(v.lightDir);
fixed3 tangentViewDir = normalize(v.viewDir);
//获取纹理法线
fixed4 packedNormal = tex2D(_BumpMap,v.uv.zw);
//如果纹理没有设置为 normal map 则需要进行计算,有则直接使用内置函数 UnpackNormal
fixed3 tangentNormal;
//tangentNormal.xy=(packedNormal.xy * 2 - 1) * _BumpScale;
//tangentNormal.z=sqrt(1.0 - saturate(dot(tangentNormal.xy,tangentNormal.xy)));
//使用内置函数能够得到任何平台下正确的计算结果
tangentNormal = UnpackNormal(packedNormal);
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 -saturate(dot(tangentNormal.xy,tangentNormal.xy)));
fixed3 albedo = tex2D(_MainTex,v.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(tangentNormal,tangentLightDir));
fixed3 halfDir = normalize(tangentLightDir+tangentViewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(tangentNormal,halfDir)),_Gloss);
return fixed4(ambient + diffuse + specular,1.0);
}
ENDCG
}
}
FALLBACK "Specular"
}
在世界空间计算
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "Unlit/brike_3"
{
Properties{
_Color("Color",Color)=(1,1,1,1)
_Specular("Specular",Color)=(1,1,1,1)
_Gloss("Gloss",Range(8,256))=20
_MainTex("MainTex",2D)="white"{}
_BumpTex("Bump",2D)="bump"{}
_BumpScale("BumpScale",Float)=2
}
SubShader{
Pass{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
fixed4 _Specular;
float _Gloss;
float _BumpScale;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpTex;
float4 _BumpTex_ST;
struct a2f{
float4 vertex:POSITION;
float4 tangent:TANGENT;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;
};
//一个插值寄存器只能存float4大小的变量
struct v2f{
float4 pos:SV_POSITION;
float4 uv:TEXCOORD0;
float4 TtoW1:TEXCOORD1;
float4 TtoW2:TEXCOORD2;
float4 TtoW3:TEXCOORD3;
};
v2f vert(a2f a){
v2f v;
v.pos=UnityObjectToClipPos(a.vertex);
v.uv.xy=a.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
v.uv.zw=a.texcoord.xy * _BumpTex_ST.xy + _BumpTex_ST.zw;
fixed3 worldPos=mul(unity_ObjectToWorld,a.vertex).xyz;
fixed3 worldNormal=UnityObjectToWorldNormal(a.normal);
fixed3 worldTangent=UnityObjectToWorldNormal(a.tangent.xyz);
fixed3 worldBinormal=cross(worldNormal,worldTangent) * a.tangent.w;
//切线空间 切线、副切线、法线
v.TtoW1=float4(worldTangent.x,worldBinormal.x,worldNormal.x,worldPos.x);
v.TtoW2=float4(worldTangent.y,worldBinormal.y,worldNormal.y,worldPos.y);
v.TtoW3=float4(worldTangent.z,worldBinormal.z,worldNormal.z,worldPos.z);
return v;
};
fixed4 frag(v2f v):SV_Target{
float3 worldPos=float3(v.TtoW1.w,v.TtoW2.w,v.TtoW3.w);
//在世界空中计算光照方向和视角方向
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
//计算切线空间下的normal
fixed3 bump = UnpackNormal(tex2D(_BumpTex,v.uv.zw));
bump.xy *= _BumpScale;
bump.z = sqrt(1.0 - saturate(dot(bump.xy,bump.xy)));
bump = normalize(half3(dot(v.TtoW1.xyz, bump), dot(v.TtoW2.xyz, bump), dot(v.TtoW3.xyz, bump)));
fixed3 albedo = tex2D(_MainTex,v.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(bump,lightDir));
fixed3 halfDir = normalize(lightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular * pow(saturate(dot(bump,halfDir)),_Gloss);
return fixed4(diffuse+ambient+specular,1.0);
}
ENDCG
}
}
FALLBACK "Specular"
}
渐变纹理
Shader "Unlit/4"
{
Properties{
_Color("Color",Color)=(1,1,1,1)
_RampTex("Ramp Tex",2D)="white"{}
_Specular("Specular",Color)=(1,1,1,1)
_Gloss("Gloss",Range(8,256))=20
}
SubShader{
Pass{
Tags {"LightModel"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
fixed4 _Specular;
float _Gloss;
sampler2D _RampTex;
float4 _RampTex_ST;
struct a2f{
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;
};
struct v2f{
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
float3 worldPos:TEXCOORD1;
float2 uv:TEXCOORD2;
};
v2f vert(a2f a){
v2f v;
v.pos=UnityObjectToClipPos(a.vertex);
v.worldNormal=UnityObjectToWorldNormal(a.normal);
v.worldPos=mul(unity_ObjectToWorld,a.vertex).xyz;
//使用内置函数 TRANSFORM_TEX 转换 纹理坐标得到 uv
v.uv=TRANSFORM_TEX(a.texcoord,_RampTex);
return v;
}
fixed4 frag(v2f v):SV_Target{
fixed3 worldNormal = normalize(v.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(v.worldPos));
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
//使用兰伯特模型光照
fixed halfLambert = 0.5 * dot(worldNormal,worldLightDir) + 0.5;
//tex2D(_RampTex,fixed2(halfLambert,halfLambert)) 使用兰伯特模型光照进行采样
fixed3 diffuse = _LightColor0.rgb * tex2D(_RampTex,fixed2(halfLambert,halfLambert)).rgb * _Color.rgb;
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(v.worldPos));
fixed3 halfDir = normalize(worldLightDir +worldViewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal,halfDir)),_Gloss);
return fixed4(ambient + diffuse + specular,1.0);
}
ENDCG
}
}
FALLBACK "Specular"
}
遮罩纹理
Shader "Customer/5"
{
Properties
{
_Color("Color",Color)=(1,1,1,1)
_MainTex("Main Tex",2D)="white"{}
_BumpMap("Bump Map",2D)="bump"{}
_BumpScale("Bump Scale",Float)=1
_Specular("Specular",Color)=(1,1,1,1)
_SpecularMask("Specular Mask",2D)="white"{}
_SpecularScale("Specular Scale",Float)=1
_Gloss("Gloss",Range(1,256))=20
}
SubShader
{
Tags { "LightMode"="ForwardBase" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;//主纹理
float4 _MainTex_ST;//公用一个纹理属性变量TextureName_ST,可以节省需要存储的纹理坐标数目。若每个都是用一个,则会很快占满
//顶点着色器中可以使用的插值寄存器。尽量使用同一种平铺和位移的操作。
sampler2D _BumpMap;//法线纹理
float _BumpScale;
fixed4 _Specular;
sampler2D _SpecularMask;//纹理遮罩
float _SpecularScale;
float _Gloss;
struct a2f{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float4 texcoord : TEXCOORD0;
};
struct v2f{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 lightDir: TEXCOORD1;
float3 viewDir : TEXCOORD2;
};
v2f vert(a2f v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//xy 缩放 zw 偏移
o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
//float3 bnormal=cross(normalize(a.normal),normalize(a.tangent.xyz)) * a.tangent.w;
//float3x3 rotation =float3x3(a.tangent.xyz,bnormal,a.normal);
//使用内置语义 得到切线空间
TANGENT_SPACE_ROTATION;
//得到切向空间下的视角方向和到光源的方向
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
return o;
};
fixed4 frag(v2f v):SV_Target{
fixed3 tangentLightDir = normalize(v.lightDir);
fixed3 tangentViewDir = normalize(v.viewDir);
fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap,v.uv));
tangentNormal.xy *=_BumpScale;
//通过xy 计算得到z
tangentNormal.z=sqrt(1.0-saturate(dot(tangentNormal.xy,tangentNormal.xy)));
fixed3 albedo = tex2D(_MainTex, v.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse=_LightColor0.rgb * albedo * saturate(dot(tangentNormal,tangentLightDir));
//使用 blinn-phong光照模型
fixed3 halfDir=normalize(tangentViewDir+tangentLightDir);
//使用rgb的任何分量效果一样,表明点对应的高光反射强度。
//得到掩码值,再用掩码值和_SpecularScale来控制高光反射的强度
//存在空间浪费,rbg都存储同一个值,在实际游戏中会充分利用遮罩纹理中的每一个颜色通道来存储不同的表面属性
fixed specularMask=tex2D(_SpecularMask,v.uv).r * _SpecularScale;
//_Gloss控制 高光点的范围大小
fixed3 specular=_LightColor0.rgb * _Specular.rgb * pow(saturate(dot(tangentNormal,halfDir)),_Gloss) * specularMask;
return fixed4(ambient+diffuse+specular,1.0);
}
ENDCG
}
}
Fallback "Specular"
}