Unity Shader学习:PBS和BRDF(基于物理着色和双向反射分布函数)

Unity Shader学习:PBS和BRDF(基于物理着色和双向反射分布函数)

PBS和BRDF涉及到关于数学光学和物理方面的知识比较多,公式也有好几套,这里先初步的了解了一下,查阅网上的资料实现了个和Unity自带Standard差不多效果的,还没有添加环境映射。其中的公式和原理还是比较复杂的不太看得懂,先拿来用着吧。

shader部分:

Shader "Custom/PBSTest" {
	Properties{
		_MainTex("MainTex",2D)="white"{}
	    _Smoothness("Smoothness",Range(0.0,0.9999))=0.5
		_Metallic("Metallic", Range(0,1)) = 0.0
		_SpecularFactor("SpecularFactor",Range(0.0,5.0)) = 1.0
		_DiffuseFactor("DiffuseFactor",Range(0.0,5.0))=0.3
	}
		SubShader{
		Pass{
		CGPROGRAM
		#include "Lighting.cginc"
		#include "UnityCG.cginc"
		#pragma vertex vert
		#pragma fragment frag

		#define Pie 3.1415926535

		float _Smoothness;
		sampler2D _MainTex;
		samplerCUBE _Cubemap;
		float _Metallic;
		float _DiffuseFactor;
		float _SpecularFactor;

		struct v2f {
		float4 pos:SV_POSITION;
		float2 uv:TEXCOORD0;
		float3 normal:TEXCOORD1;
		float3 worldPos:TEXCOORD2;
        };

		struct OutPut {
			float3 Albedo;
			float3 normalDir;
			float3 halfDir;
			float Metallic;
			float Smoothness;
			float3 viewDir;
			float3 lightDir;
		};

		//平方
		float sqr(float value) {
			return value * value;
		}
		//微面元遮挡函数
		float G(float roughness,float NdotL,float NdotV) {
			float alpha = sqr(roughness);			
			float lambdaV = NdotL * sqrt((-NdotV * alpha + NdotV) * NdotV + alpha);
			float lambdaL = NdotV * sqrt((-NdotL * alpha + NdotL) * NdotL + alpha);
			return 0.5 / (lambdaL + lambdaV + 0.00001);
		}
		//微法线分布函数
		float D(float roughness,float NdotH){
			float alpha = sqr(roughness);
			float denominator = sqr(NdotH) * (alpha - 1.0) + 1.0f;
			return alpha / (Pie * sqr(denominator));
		}
		//菲涅尔反射
		float3 F_Term(float3 F0, float VdotH) {
			return F0 + (1 - F0)*pow(1 - VdotH, 5);
		}
		float3 F_Lerp(float3 F0,float3 F90,float VdotH) {
			return lerp(F0, F90, pow(1 - VdotH, 5));
		}
		//漫反射
		float DisneyDiffuse(float NdotV, float NdotL, float LdotH, float roughness) {
			float FD90 = 0.5 + 2 * sqr(LdotH)*roughness;
			float FL = 1 + (FD90 - 1)*pow(1 - NdotL, 5);
			float FV = 1 + (FD90 - 1)*pow(1 - NdotV, 5);
			return FL * FV*_DiffuseFactor*Pie;
		}

		float4 BRDF(OutPut o, float3 SpecularColor, float oneMinusReflectivity) {
			float roughness = 1 - o.Smoothness;
			float NdotH = saturate(dot(o.normalDir, o.halfDir));
			float LdotH = saturate(dot(o.lightDir, o.halfDir));
			float NdotL = saturate(dot(o.normalDir, o.lightDir));
			float VdotH = saturate(dot(o.viewDir, o.halfDir));
			float NdotV = abs(dot(o.normalDir, o.viewDir));
			float diffuseTerm = DisneyDiffuse(NdotV, NdotL, LdotH, roughness)*NdotL;
			float V = G(roughness, NdotL, NdotV);
			float D_1 = D(roughness, NdotH);
			float specularTerm = V * D_1*Pie;
			specularTerm = max(0, specularTerm*NdotL);
			float F = F_Term(SpecularColor, LdotH);
			float surfaceReduction = 1.0 / (roughness*roughness + 1.0);
			specularTerm *= any(SpecularColor)?1.0:0.0;
			float grazingTerm = saturate(o.Smoothness + (1 - oneMinusReflectivity));
			float3 Fidentity = F_Lerp(SpecularColor, grazingTerm,VdotH);
			float3 color = o.Albedo*( _LightColor0.rgb*diffuseTerm + 0.5) + specularTerm * _LightColor0.rgb*F*_SpecularFactor + 0.5*surfaceReduction * Fidentity;
			return float4(color, 1);
		}

		float4 LightingStandard(OutPut o) {
			//unity_ColorSpaceDielectricSpec里面存的是Unity选用的电介质反射率,alpha通道是1-dielectricSpec
			float3 specColor = lerp(unity_ColorSpaceDielectricSpec.rgb, o.Albedo, o.Metallic);
			float3 oneMinusReflectivity = unity_ColorSpaceDielectricSpec.a*(1-o.Metallic);
			o.Albedo = o.Albedo*oneMinusReflectivity;
			return BRDF(o, specColor, oneMinusReflectivity);
		}

	    v2f vert(appdata_full v) {
			v2f o;
			o.pos = UnityObjectToClipPos(v.vertex);
			o.uv = v.texcoord;
			o.normal= UnityObjectToWorldNormal(v.normal);
			o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
			return o;
		}

		float4 frag(v2f i) :SV_Target{
			float3 worldNormalDir = normalize(i.normal);
			float3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);		
			float3 worldViewDir= normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
			float3 worldHalfDir = normalize(worldLightDir + worldViewDir);

			OutPut o;
			o.Albedo = tex2D(_MainTex, i.uv);
			o.Metallic = _Metallic;
			o.Smoothness = _Smoothness;
			o.normalDir= worldNormalDir;
			o.viewDir = worldViewDir;
			o.lightDir = worldLightDir;
			o.halfDir = worldHalfDir;
			return LightingStandard(o);
		}
		ENDCG
        }	
	}
	FallBack "Diffuse"
}

你可能感兴趣的:(Unity,Shader)