读一读

123.png


Tags{ "Queue"="AlphaTest" }


在Unity中有两种方式可以实现透明效果:一是使用透明度测试(Alpha Test)、一个是透明度混合(Alpha Blending)。要实现透明的效果就需要注意渲染的顺序了,在没有透明物体的时候,渲染顺序是不重要的,因为有一个深度测试,就是离摄像机的远近程度,深度值小的颜色值就会覆盖深度大的buff,反之直接舍弃了,所以无论怎么渲染都是对的。但是如果有透明物体后,透明物体就应该在不透明的物体后面渲染,在渲染透明物体时要关闭深度写入(更新深度值buff)。对于透明物体需要注意重叠的情况,尽量细分模型。

常用的办法是:

  1. 先渲染所有不透明物体,并开启它们的深度测试和深度写入。

  2. 把半透明物体按它们距离摄像机的远近进行排序,然后按照从后往前的循序渲染这些半透明物体,并开启他们的深度测试,但关闭深度写入。


遮罩纹理是为了具体的控制某些光照的影响,也就是可以逐像素的控制表面的属性。比如说,一张纹理有4个通道RGBA,我们可以利用A通道存储高光反射的强度,G通道存储边缘光照的强度,B通道存储高光反射的指数,A通道存储自发光的强度。这样,使用遮罩纹理就能很好的控制每一个顶点受到的光照影响。


Shader "Unlit/jianbian"
{
	Properties
	{
		_Color ("Color",Color) = (1,1,1,1)
		_RampTex ("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Tags { "LightMode"="ForwardBase" }

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
			#include "Lighting.cginc" 

			struct appdata
			{
				float4 vertex : POSITION;
				float3 normal:NORMAL;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float3 worldNormal:TEXCOORD1;
				float3 worldPos:TEXCOORD2;
			};

			fixed4 _Color;
			sampler2D _RampTex;
			float4 _RampTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				fixed z = 0.5 * dot(worldNormal, worldLightDir) + 0.5;

				//利用光照影响因子构建uv来取样渐变纹理,从而影响漫反射
				fixed3 diffuse = tex2D(_RampTex, fixed2(z, z)).rgb * _Color.rgb * _LightColor0.rgb;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

				return fixed4( diffuse.rgb + ambient.rgb,1);
			}
			ENDCG
		}
	}
}


TIM图片20180926175152.pngTIM图片20180926175221.pngTIM图片20180926175429.png


在法线贴图导入到Unity后,可以设置为Normal map类型,这一步操作Unity会根据平台来看看是否对法线贴图进行压缩,随后中需要使用UnpackNormal函数来映射为法线。

inline fixed3 UnpackNormalDXT5nm (fixed4 packednormal)
{    
    fixed3 normal;    
    normal.xy = packednormal.wy * 2 - 1;    
    normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));//根号(1-(x^2 + y^2))
    return normal;
}

inline fixed3 UnpackNormal(fixed4 packednormal)
{
    #if defined(UNITY_NO_DXT5nm)    
        return packednormal.xyz * 2 - 1;
    #else    
        return UnpackNormalDXT5nm(packednormal);
    #endif
}


法线可以压缩,只用两个通道就可以的原因在于:

一、法线是一个单位向量,也就是x^2+y^2+z^2=1

二、法线是一个只有正方向的向量


a2v中的数据,传入的就是顶点的法线和切线都是模型空间中的,但是切线空间是顶点为中心的,那么可以说这些顶点的法线和切线就是切线空间x,z轴在模型空间的表达,那么就很好求出切线空间到模型空间的变换矩阵了,只需要使用这几个轴构建变换矩阵就行了。那么模型到切线的变换矩阵就可以通过求逆来得到了,切到模只有旋转和平移,那么就可以通过转置来直接获取得到了。

//计算副切线的方向,v.tangent.w记录了副切线的方向
float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w;
//转置构建变换矩阵
float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);


也可以使用Unity内置的宏来计算出rotation

TANGENT_SPACE_ROTATION;
//rotation

Wrap Mode主要有两种:Repeat为平铺模式,uv超过1范围后会截去整数部分。Clamp为限制模式,当uv超过范围后,就会限制在最小或最大值。

Fiter Mode有三种模式:Point为直接取样最接近的纹理坐标颜色,Bilinear为取样附近4个像素进行线性插值混合,Trilinear和Bilinear再没有使用Mipmapping技术时是一样的。

使用Minmapping前提下,不同的滤波:

cb4.png


a1a9e5.png


struct appdata_full 
{    
    float4 vertex : POSITION;    
    float4 tangent : TANGENT;    
    float3 normal : NORMAL;    
    float4 texcoord : TEXCOORD0;
    float4 texcoord1 : TEXCOORD1;    
    float4 texcoord2 : TEXCOORD2;    
    float4 texcoord3 : TEXCOORD3;
        #if defined(SHADER_API_XBOX360)    
        half4 texcoord4 : TEXCOORD4;    
        half4 texcoord5 : TEXCOORD5;
        #endif    
    fixed4 color : COLOR;
};

123.png


在PC平台,GPU也许会将这三种类型都会当成最大精度的来处理,但是在移动平台可能就不会了。尽量使用小的类型来存储可以提升性能,例如可以用fixed来存储颜色信息和单位矢量等!


888.png