一、需要提前了解的
纹理可以理解为就是一张图片,对于一张平面图,如何“铺”在一个不规则的物体上,是第一个要考虑的问题
- 纹理基础属性及纹理坐标:https://blog.csdn.net/Jaihk662/article/details/106062221
- 纹理与光照:https://blog.csdn.net/Jaihk662/article/details/106674211
只需要了解里面的理论部分就好了
二、给物体铺上外皮
再复杂的物体都是由一个个三角形面片拼接而成的,仅考虑其中一个三角形面片,若要将纹理“铺”在这个三角形面上,那么只需要在纹理中确定三个点(纹理坐标)对应三角形面片的三个顶点“贴”上去就可以了,就像我们平时贴贴纸一样,只不过相对于贴纸,纹理可以拉伸和越界
- 标准化纹理坐标:纹理的大小是不确定的,例如很多远古的游戏场景都看起来比较模糊,其纹理大小可能只有 256 x 256,而现在很多看起来画面非常不错的游戏,最大的纹理精度甚至可以达到 4096 x 4096,因此我们需要将这些纹理的坐标归一化到 [0, 1] 的范围内,然后再进行纹理采样,这样就统一了纹理坐标的输入,图片是二维的,所以这些坐标也会是二维坐标(UV 坐标)。前面记录的 OpenGL(原点左下) 和 DirectX(原点左上) 在二维纹理空间中的坐标系差异问题,就体现在这
- 纹理坐标越界处理:纹理坐标范围是 [0, 1],在计算的过程中,采样坐标可能超过这个范围,这个时候就需要通过纹理的环绕方式设置来进行映射,将坐标按照对应的算法映射回 [0, 1] 范围内再进行采样
事实上,各种纹理贴图的分辨率高不高是决定一个游戏画质的主要因素
三、最简单的Shader
一般来讲,在给物体贴上纹理之前,是需要考虑光照的,在完全没有光的环境下,物体应为完全的黑暗
对于物体本身的纹理,其往往同时作为漫反射纹理和环境光纹理:
Shader "Jaihk662/NewSurfaceShader"
{
Properties
{
_DiffuseColor ("DiffuseColor", Color) = (1.0, 1.0, 1.0, 1.0)
_SpecularColor ("SpecularColor", Color) = (1.0, 1.0, 1.0, 1.0)
_MainTex ("MainTex", 2D) = "white" {}
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader
{
LOD 200
PASS
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert //声明顶点着色器的函数
#pragma fragment frag //声明片段着色器的函数
#include "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _DiffuseColor;
fixed4 _SpecularColor;
sampler2D _MainTex;
float4 _MainTex_ST;
float _Gloss;
struct _2vert
{
float4 vertex: POSITION;
float3 normal: NORMAL;
float4 texcoord: TEXCOORD0;
};
struct vert2frag
{
float4 pos: SV_POSITION;
float3 wPos: TEXCOORD0;
float3 wNormal: TEXCOORD1;
float2 uv: TEXCOORD2;
};
vert2frag vert(_2vert v)
{
vert2frag o;
o.pos = UnityObjectToClipPos(v.vertex);
o.wNormal = mul(v.normal, (float3x3)unity_WorldToObject);
o.wPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(vert2frag i): SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * tex2D(_MainTex, i.uv).rgb * _DiffuseColor.rgb;
fixed3 w1Normal = normalize(i.wNormal);
fixed3 wLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * tex2D(_MainTex, i.uv).rgb * _DiffuseColor.rgb * saturate(dot(w1Normal, wLightDir));
fixed3 reflectDir = normalize(reflect(-wLightDir, w1Normal));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.wPos));
fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
- TRANSFORM_TEX(uv, tex):将顶点对应的 uv 坐标与缩放(Tiling)、偏移(Offset)两个变量进行运算,以得到实际的顶点 uv
在 Unity 中,可以使用“纹理名_ST”的方式来声明某个纹理的属性。其中 ST 就是缩放(Tiling)和偏移(Offset)的缩写。上面代码中纹理名起为 _MainTex,那么 _MainTex_ST 就可以让我们得到该纹理的缩放偏移值,其中 _MainTex_ST.xy 存储的是缩放值,_MainTex_ST.zw 存储的是偏移值,这些值可以在材质面板的纹理属性中调节:
四、纹理属性
随便选择一张图片,可以发现它有好多好多的属性/选项:
一个一个来看:
- TextureType:贴图类型,是否为光照贴图/法线贴图/精灵等,默认为 Default:同理于之前的 Texture,是最常见纹理类型,可以用于所有纹理,提供大多数属性的设置
- TextureShape:是否为立方体贴图
- sRGB:启用此属性纹理将存储在gamma空间中
- AlphaSource:指定如何生成纹理的alpha通道
- AlphaIsTransparency:启用后如果对应像素的 alpha 通道是透明的,启用此属性可无视掉对应的RGB
- Wrap Mode:环绕方式,越界采样
- Filter Mode:纹理过滤方式,马赛克风还是模糊处理,Trilinear 相对于 Bilinear 还会在多级渐远纹理之间进行混合
MipMaps相关:多级渐远纹理,可以后面了解
平台相关:对于不同的平台,可以对资源的质量和尺寸做不同的考虑与设置,不过想要对其它的平台的设置进行调整,需要安装对应的 Unity 平台支持