在看shader入门精要。

第六章 Unity中的基础光照

半兰伯特模型(Half Lambert)

6.5 高光反射光照模型

逐顶点光照

逐像素光照

Blinn-Phong光照模型

使用Unity内置的函数

unity ShaderLab 基础之【UnityCG.cginc 库】命令详解

第七章 基础纹理

实践

Properties {
	_Color ("Color Tint", Color) = (1,1,1,1)
	_MainTex ("Main Tex", 2D) = "white" {}
	_Specular ("Specular", Color) = (1,1,1,1)
	_Gloss ("Gloss", Range(8.0,256)) = 20
}
//CG代码中声明
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST; //这个是需要的
fixed4 _Specular;
float _Gloss;

_MainTex_ST中的ST是scale和translation,通过它得到缩放和平移值。

_MainTex_ST.xy存储缩放值

_MainTex_ST.zw存储偏移值

struct a2v {
	float4 vertex : POSITION;
	float3 normal : NORMAL;
	float4 texcoord : TEXCOORD0;
}
struct v2f {
	float4 pos : SV_POSITION;
	float3 worldNormal : TEXCOORD1;
	float2 uv : TEXCOORD2;
}
v2f vert(a2v v) {
	v2f o;
	o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
	o.worldNormal = UnityObjectToWorldNormal(v.normal);
	o.worldPos = mul(_Object2World, v.vertex).xyz;
	o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_St.zw;
	// or 
	//o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
	return o;
}
fixed4 frag(v2f i) : SV_Target {
	fixed3 worldNormal = normalize(i.worldNormal);
	fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
	
	fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb; // 反射率;漫反射系数
	fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; // 环境光
	fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir)); // 漫反射
	fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
	fixed3 halfDir = normalize(worldLightDir + viewDir);
	fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
	return fixed4(ambient + diffuse + specular, 1.0);
}

纹理的属性

Texture Type: Texture, Normal map, Cubemap

Wrap Mode: Repeat, Clamp。当纹理坐标超过[0,1]范围后如何平铺。

Filter Mode: 当纹理由于变换而产生拉伸时采用哪种滤波。Point,Biliner,Trilinear。

纹理缩小的过程比放大更加复杂一些。多级渐远纹理技术(minmapping),提前用滤波处理来得到很多更小的图像,形成了一个图像金字塔,每一层都是对上一层图像降采样的结果。但会多占用33%的内存。纹理导入中,将纹理类型(Texture Type)选择成Advanced,再勾选Generate Mip Maps即可开启多级渐远纹理技术。

纹理的最大尺寸和纹理模式。

用了大量Truecolor类型纹理会增加内存。

凹凸映射bump mapping

不会真的改变模型的顶点位置,只是让模型看起来好像是凹凸的。

有2种实现方法:

高度纹理height mapping,使用高度图存储强度值,表示模型的表面高度。但是计算复杂。

法线纹理normal mapping。

将修改后的模型空间中的表面法线存储在一张纹理中,称为模型空间的法线纹理(object-space normal map)。实际制作中会用另一种坐标空间,切线空间(tangent space)来存储法线。

模型空间存储法线的优点:

  • 实现简单,直观。
  • 可见的突变缝隙较少。

切线空间优点更多:

  • 自由度很高
  • 可进行UV动画。
  • 可重用法线纹理
  • 可压缩

实践

我们需要在计算光照模型中统一各个方向矢量所在的坐标空间。

一种是在切线空间下进行光照计算,一种是在世界空间下进行光照计算。

计算前需要变换到统一的坐标系(空间)。还可以是模型空间。

在切线空间下进行光照计算

基本思路是:在片元着色器中通过纹理采样得到切线空间下的法线,然后再与切线空间下的视角方向、光照方向等进行计算,等到最终的光照结果。

Shader "somename" {
Properties {
	_Color ("Color Tint", Color) = (1,1,1,1)
	_MainTex ("Main Tex", 2D) = "white" {}
	_BumpMap ("Normal Map", 2D) = "bump" {} // "bump"是unity内置法线纹理
	_BumpScale ("Bump Scale", Float) = 1.0
	_Specular ("Specular", Color) = (1,1,1,1)
	_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader {
	Pass {
		Tags {"LightMode"="ForwardBase"}
		CGPROGRAM
		
		#pragma vertex vert
		#pragma fragment frag
		
		#inlcude "Lighting.cginc"
		
		fixed _Color;
		sampler2D _MainTex;
		float4 _MainTex_ST;
		sampler2D _BumpMap;
		float4 _BumpMap_ST;
		float _BumpScale;
		fixed4 _Specular;
		float _Gloss;
		
		struct a2v {
			float4 vertex : POSITION;
			float3 normal : NORMAL;
			float4 tangent :TANGENT; // tangent.w分量是决定切线空间z坐标轴--副切线的方向性
			float4 texcoord : TEXCOORD0;
		}
		
		struct v2f {
			float4 pos : SV_POSITION;
			float4 uv : TEXCOORD0;
			float3 lightDir : TEXCOORD1;
			float3 viewDir : TEXCOORD2;
		}
		
		v2f vert(a2v v) {
			v2f o;
			o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
			
			o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; //计算缩放和平移后的坐标
			o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
			
			// Compute the binormal
			// float3 binormal = cross(normalize(v.normal), normalize(v.tangent.xyz)) * v.tangent.w;
			//Construct a matrix which transform vectors from object space to tangent space
			//float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
			//Or just use the built-in macro
			TANGENT_SPACE_ROTATION;
			
			o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
			o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
			
			return 0;
		}
		
		fixed4 frag(v2f i) : SV_Target {
			fixed3 tangentLightDir = normalize(i.lightDir);
			fixed3 tangentViewDir = normalize(i.viewDir);
			
			//Get the texel in the normal map
			fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw);
			fixed3 tangentNormal;
			//If the texture is not marked as "Normal Map"
            //tangentNormal.xy = (packedNormal.xy*2 -1) * _BumpScale;
            //tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
            
            //Or mark the texture as "Normal Map", and use the built-in funtion
            tangentNormal = UnpackNormal(packedNormal);
            tangentNormal.xy *= _BumpScale;
            tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangeNormal.xy)));
            
            fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
            fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));
            fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
            fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss);
            
            return fixed4(ambient + diffuse + specular, 1.0);
		}
		
        
		ENDCG
	}
    
}
Fallback "Specular"
}

2.在世界空间下计算


		fixed4 frag(v2f i) : SV_Target {
			//get the position in world space
			float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
			//
			fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
			fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
			
			fixed3 bump = UnpackNormal(tex2d(_BumpMap, i.uv.zw));
			bump.xy *= _BumpScale;
			bump.z = sqrt(1.0 - saturate(dot(bump.xy, bump.xy)));
			bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
			///////
			
			。。。。
		}
		

7.4 遮罩纹理

Shader "Mask Texture" {
Properties {
    _Color ("Color Tint", Color) = (1,1,1,1)
    _MainTex ("Main Tex", 2D) = "white" {}
    _BumpMap ("Normal Map", 2D) = "bump" {}
    _BumpScale ("Bump Scale", Float) = 1.0
    _SpecularMask ("Specular Mask", 2D) = "white" {}
    _SpecularScale ("Specular Scale", Float) = 1.0
    _Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader {
    Pass {
        Tags { "LightMode"="ForwardBase" }
        CGPROGRAM
        
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
        fixed4 _Color;
        sampler2D _MainTex;
        float4 _MainTex_ST;
        sampler2D _BumpMap;
        float _BumpScale;
        Sampler2D _SpecularMask;
        float _SpecularScale;
        fixed4 _Specular;
        float _Gloss;
            
        struct a2v {
            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(a2v v) {
            v2f o;
            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
            o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
            TANGENT_SPACE_ROTATION;
            o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
            o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz
            return o;
        }
        
        fixed4 frag(v2f i) : SV_Target {
            fixed3 tangentLightDir = normalize(i.lightDir);
            fixed3 tangentViewDir = normalize(i.viewDir);
            fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uv));
            tangentNormal.xy *= _BumpScale;
            tangentNormal.z = sqrt(1.0-saturate(dot(tangentNormal.xy, tangentNormal.xy)));
            fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
            fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));
            fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
            fixed specularMask = tex2D(_SpecularMask, i.uv).r * _SpecularScale;
            fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss) * specularMask;
            return fixed4(ambient + diffuse + specular, 1.0);
        }
        
        ENDCG
    }
}
}