【KaJiYa改进】Shader头发效果

感谢原作者,链接见转载。
头发各项异性高光:该shader需使用三张texture进行合成,可以像KaJiYa调节光照环,另外也可以调节Tilling影响发丝,立方体纹理影响整体效果。

在这里插入图片描述

Shader "Unlit/CharHair"
{
	Properties
	{
		_BaseMap ("BaseMap", 2D) = "white" {}
		_CompMask ("ComMase", 2D) = "white" {}
		_NormalMap("bumpMap",2D) = "bump"{}
		_SpecIntensity("_SpecIntensity",Range(0,20))= 1
		_SpecShininess("_SpecShininess",Range(0,20))= 1

		[Header(IBL)]
		_EvnMap("cube map",Cube) = "white"{}
		_Tint("tint",Color) = (1,1,1,1)
		_Expose("Expose",float) = 1
		_Rotate("Rotate",Range(0,360)) = 0


[Header(Aniso)]
		_AnisoMap("anis",2D) = "gray"{}

		_SpecColor1("specColor 1",Color) = (1,1,1,1)
		_SpecShininess1("_SpecShininess 1",Range(0,1)) = 0.1
		_SpecNoise1("_SpecNoise1",float) = 1
		_SpecOffset1("_Specffset1",float) = 0

		_SpecColor2("specColor 2",Color) = (1,1,1,1)
		_SpecShininess2("_SpecShininess 2",Range(0,1)) = 0.1
		_SpecNoise2("_SpecNoise 2",float) = 1
		_SpecOffset2("_Specffset 2",float) = 0


_RoughnessAdjust("_RoughnessAdjust",Range(0,1)) = 1

	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			Tags{ "LightMode"="ForwardBase"}
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_fwdbase

			// 这是两种写法 
			#pragma shader_feature _SPECCHECK_ON
			#pragma multi_compilr _ _IBLCHECK_ON
			
			#include "UnityCG.cginc"
			#include "AutoLight.cginc"
			#include "Lighting.cginc"
			struct appdata
			{
				float4 vertex : POSITION;
				float2 texcoord : TEXCOORD0;
				float3 normal : NORMAL;
				float4 tangent : TANGENT;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 pos : SV_POSITION;
				float3 pos_world : TEXCOORD1;

				float3 normal_dir : TEXCOORD2;
				float3 tangent_dir : TEXCOORD3;
				float3 binormal_dir : TEXCOORD4;
			};

			sampler2D _BaseMap;
			float4 _BaseMap_ST;
			sampler2D _NormalMap;
			
			float _SpecShininess;
			float4 _SpecIntensity;
			
			float _RoughnessAdjust;
			// ibl
			samplerCUBE _EvnMap;
			float4 _EvnMap_HDR;
			float4 _Tint;
			float _Expose;

			//  aniso
			sampler2D _AnisoMap;
			float4 _AnisoMap_ST;
			float4 _SpecColor1;
			float _SpecNoise1;
			float _SpecShininess1;
			float _SpecOffset1;

						
			float4 _SpecColor2;
			float _SpecNoise2;
			float _SpecShininess2;
			float _SpecOffset2;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.texcoord, _BaseMap);
				o.normal_dir = normalize(UnityObjectToWorldNormal(v.normal));
				o.tangent_dir = normalize(UnityObjectToWorldDir(v.tangent.xyz));
				o.binormal_dir = normalize(cross(o.normal_dir, o.tangent_dir)) * v.tangent.w;
				o.pos_world	= mul(unity_ObjectToWorld,v.vertex).xyz;
				
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{

				fixed4 base_color_ = tex2D(_BaseMap, i.uv);
				fixed4 base_color = base_color_;
				fixed4 spec_color = base_color_;
				half roughtness = saturate(_RoughnessAdjust);
				fixed3 normal_data = UnpackNormal(tex2D(_NormalMap,i.uv));


				half3 light_dir = normalize(UnityWorldSpaceLightDir(i.pos_world));
				half3 view_dir = normalize(UnityWorldSpaceViewDir(i.pos_world));
				
				half3 normal_dir = normalize(i.normal_dir);
				half3 tangent_dir = normalize(i.tangent_dir);
				half3 binormal_dir = normalize(i.binormal_dir);
				
				float3x3 TBN = float3x3(tangent_dir,binormal_dir,normal_dir);
				normal_dir = normalize(mul(normal_data.xyz,TBN));

				
				// Direct diffuse 直接光漫反射
				//half diff_term = saturate(dot(light_dir,normal_dir));
				half diff_term = dot(light_dir,normal_dir);
				// 半兰伯特模型: 将【-1,1】 映射到【0,1】,  对于负数(背光面),也会右明暗变化!!!!!!
				half half_lambert = (diff_term + 1) * 0.5;
				half direct_diffuse = diff_term * _LightColor0.xyz * base_color.xyz;

				//Direct specular 直接光 镜面反射
				half2 uv_aniso = i.uv * _AnisoMap_ST.xy + _AnisoMap_ST.zw;
				half aniso_noise = tex2D(_AnisoMap,uv_aniso).r - 0.5;

				half3 half_dir = normalize(light_dir + view_dir);
				half NdotH = saturate(dot(normal_dir,half_dir));
				half TdotH = dot(tangent_dir,half_dir);
				half NdotV = saturate(dot(view_dir,normal_dir));

				// 求出各项异性高光的 衰减值 
				float aniso_atten = saturate(sqrt(saturate(half_lambert/NdotV)));
				// spec1
				//  加上baseColor  ,有一个渐变的过渡
				float3 spec_color1 = _SpecColor1.rgb + base_color;

				float3 aniso_offset1 = normal_dir * (aniso_noise * _SpecNoise1 + _SpecOffset1);

				binormal_dir = normalize(binormal_dir + normal_dir * aniso_offset1);
				// 除以光滑度
				float BdotH1 = dot(binormal_dir,half_dir)/ _SpecShininess1;

				// float3 kajiya = sqrt(1 - BdotH1 * BdotH1)
				float spec_term1 = exp(-(TdotH * TdotH + BdotH1 * BdotH1)/ (1+ NdotH));
				float3 final_spec1 = spec_term1 * aniso_atten * spec_color1 * _LightColor0.xyz;

				// spec2
				float3 spec_color2 = _SpecColor2.rgb + base_color;
				float3 aniso_offset2 = normal_dir * (aniso_noise * _SpecNoise2 + _SpecOffset2);
				binormal_dir = normalize(binormal_dir + normal_dir * aniso_offset2);
				float BdotH2 = dot(binormal_dir,half_dir)/ _SpecShininess2;
				float spec_term2 = exp(-(TdotH * TdotH + BdotH2 * BdotH2)/ (1+ NdotH));
				float3 final_spec2 = spec_term2 * aniso_atten * spec_color2 * _LightColor0.xyz;


				// 正常的镜面反射
				// half3 half_dir = normalize(light_dir + view_dir);
				// half NdotH = saturate(dot(normal_dir,half_dir));
				// half smoothness = 1- roughtness;
				// half shininess = lerp(1,_SpecShininess,smoothness);
				// half spec_term = pow(NdotH,shininess);
				// half3 direct_specular = spec_term * spec_color * _LightColor0.rgb * _SpecIntensity;



				// InDirect spec 间接光镜面反射
				float3 reflect_dir = reflect(-view_dir,normal_dir);

				roughtness = roughtness * (1.7 - 0.7 * roughtness);
				float mip_level = roughtness * 6;
				half4 color_cubemap = texCUBElod(_EvnMap,float4(reflect_dir,mip_level));
				float3 evn_color = DecodeHDR(color_cubemap,_EvnMap_HDR);
				// 用系数 乘以 金属色
				half3 evn_specular = evn_color * _Expose * spec_color;
				
				//float3 final_color = direct_diffuse + direct_specular  + evn_specular;
				float3 final_color = direct_diffuse + final_spec2 + final_spec1 + evn_specular;

				

				return fixed4(final_color,1);
			}
			ENDCG
		}
	}
}


头发各向异性渲染Shader 这个是04年的一个ppt,主要介绍了头发的渲染,其追到源头还是要看这个原理。 各向异性的主要计算公式: 主要代码如下: 切线混合扰动部分(这部分也可以用T+k*N,来对切线进行扰动): float3x3 tangentTransform = float3x3(i.tangentDir, i.bitangentDir, i.normalDir); float3 _T_var = UnpackNormal(tex2D(_Tangent, TRANSFORM_TEX(i.uv0, _Tangent))); float3 temp = lerp(_TangentParam.xyz, _T_var, _BlenfTangent); float3 T = normalize(mul(float3(temp.xy,0), tangentTransform)); 主要是通过改变切线的xy值来造成头发高光部分的多样性。 高光部分,按公式计算即可: float StrandSpecular(float3 T, float3 V, float3 L, float exponent) { float3 H = normalize(L + V); float dotTH = dot(T, H); float sinTH = sqrt(1 - dotTH*dotTH); float dirAtten = smoothstep(-1, 0, dotTH); return dirAtten*pow(sinTH, exponent); } 注意,为了模拟的更贴近真实性,应用两层高光,第一层高光代表直射光直接反射出去,第二层代表次表面散射现象具体看代码。 最终渲染部分: float4 HairLighting(float3 T, float3 N, float3 L, float3 V, float2 uv, float3 lightColor) { float diffuse = saturate(lerp(0.25, 1.0, dot(N, L)))*lightColor; float3 indirectDiffuse = float3(0, 0, 0); indirectDiffuse += UNITY_LIGHTMODEL_AMBIENT.rgb; // Ambient Light float3 H = normalize(L + V); float LdotH = saturate(dot(L, H)); float3 specular = _Specular*StrandSpecular(T, V, L, exp2(lerp(1, 11, _Gloss))); //float specMask = tex2D(_SpecMask, TRANSFORM_TEX(uv, _SpecMask)); specular += /*specMask*/_SubColor*StrandSpecular(T, V, L, exp2(lerp(1, 11, _ScatterFactor))); float4 final; float4 base = tex2D(_MainTex, TRANSFORM_TEX(uv, _MainTex)); float3 diffuseColor = (_Color.rgb*base.rgb); //float ao = tex2D(_AO, TRANSFORM_TEX(uv, _AO)).g; final.rgb = (diffuse + indirectDiffuse)*diffuseColor + specular*lightColor* FresnelTerm(_Specular, LdotH); //final.rgb *= ao; final.a = base.a; clip(final.a - _CutOff); return final; } 这里我注释掉了AO和高光遮罩,需要的同学可以加上。 最后一点为了不让头发的边经过clip之后太硬,需要进行两个通道的belnd。 第二个pass使用以下指令: Blend SrcAlpha OneMinusSrcAlpha ZWrite Off 注意第二个通道无需再进行clip操作。 至此,头发渲染完毕。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值