Matcap Shader 详解【3】 - 利用Matcap实现基本PBR光照模型

匿名 (未验证) 提交于 2019-12-03 00:22:01

前言

第一篇文章中提到 基础Matcap 的另一个缺陷是缺少经典BPR所具备的一些属性,本文的目的便是为了探讨如何解决这个缺陷。

第二篇文章中,我们看到在没有使用 固有色(Albedo)贴图 时,Matcap的确能呈现出PBR效果,但与固有色叠加后,便会显得暗淡――这源于漫反射衰减与高光的缺失。


另一方面,Matcap是完全不考虑光照影响的渲染方法,因此也不存在能量守恒,只能通过采样贴图的绘制做出能量守恒的效果,所以不是真正的PBR,也因此能做出很多PBR无法实现的效果。

漫反射补偿

很多已有的Matcap采样图都是BPR渲染的结果,我们获得的固有色贴图也是衰减后的状态(比真正的固有色暗)。当这两种贴图采样相乘后,自然会显得很暗,这个问题也很容易处理,便是补偿损失的漫反射强度――对Matcap Diffuse采样图乘以大于1的数值,如下图所示:


高光

补偿后就不存在亮度的问题了,但这里还存在一个问题――高光颜色受到固有色影响。

本质原因在于MatcapDiffuse贴图理论上只用于计算漫反射以及次表面散射等现象(乘法)。但高光的计算应使用加法。

所以只需要在现有着色器上再加入一个 高光Matcap采样图,并把它加到现有的结果上。(注:PS中的线性减淡就是加法)


上图中高光的颜色就正常了许多。这里还要说明一点,虽然上例中MatcapDiffuse和MatcapSpec用了同一张贴图,但理论上正确的方法是Diffuse只画漫反射部分,Spec只画高光部分。当然,有时错误的方法效果也不错~

下图展示了分开绘制漫反射和高光的效果:


镜面反射与菲涅尔现象

Spec项作为加法项,很适合用于镜面反射及菲涅尔现象。菲涅尔的具体定义大家可以自行查询,Matcap是基于视觉法线的,所以天然与菲涅尔切合。

这里分别给出一个镜面反射(彩色金属)和一个菲涅尔反射(车漆效果)的例子,大家可以参考例子中的采样图:



有了Diffuse贴图和Spec贴图的支持后,很容易实现不同金属度、光滑度的效果,这里就不一一举例了。

下面附上这一章的源码:

Shader "TJia/Matcap_PBR" { 	Properties { 		_Color ("Main Color", Color) = (1,1,1,1) 		_MainTex("Albedo Tex", 2D) = "white" {} 		_BumpMap ("Normal Tex", 2D) = "bump" {} 		_BumpValue ("Normal Value", Range(0,10)) = 1 		_MatCapDiffuse ("MatCap Diffuse (RGB)", 2D) = "white" {} 		_DiffuseValue ("Diffuse Value", Range(0,5)) = 1 		_MatCapSpec ("MatCap Spec (RGB)", 2D) = "white" {} 		_SpecValue ("Spec Value", Range(0,2)) = 0 	} 	 	Subshader { 		Tags { "RenderType"="Opaque" } 		 		Pass { 			Tags { "LightMode" = "Always" } 			 			CGPROGRAM 				#pragma vertex vert 				#pragma fragment frag 				#include "UnityCG.cginc" 				 				struct v2f {  					float4 pos : SV_POSITION; 					float4	uv : TEXCOORD0; 					float3	TtoV0 : TEXCOORD1; 					float3	TtoV1 : TEXCOORD2; 				};  				uniform float4 _BumpMap_ST; 				uniform float4 _MainTex_ST; 				 				v2f vert (appdata_tan v) 				{ 					v2f o; 					o.pos = UnityObjectToClipPos (v.vertex); 					o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex); 					o.uv.zw = TRANSFORM_TEX(v.texcoord,_BumpMap); 					 					 					TANGENT_SPACE_ROTATION; 					o.TtoV0 = normalize(mul(rotation, UNITY_MATRIX_IT_MV[0].xyz)); 					o.TtoV1 = normalize(mul(rotation, UNITY_MATRIX_IT_MV[1].xyz)); 					return o; 				} 				 				uniform fixed4 _Color; 				uniform sampler2D _BumpMap; 				uniform sampler2D _MatCapDiffuse; 				uniform sampler2D _MainTex; 				uniform sampler2D _MatCapSpec; 				uniform fixed _BumpValue; 				uniform fixed _DiffuseValue; 				uniform fixed _SpecValue; 				 				float4 frag (v2f i) : COLOR 				{ 					fixed4 c = tex2D(_MainTex, i.uv.xy); 					float3 normal = UnpackNormal(tex2D(_BumpMap, i.uv.zw)); 					normal.xy *= _BumpValue; 					normal.z = sqrt(1.0- saturate(dot(normal.xy ,normal.xy))); 					normal = normalize(normal); 					 					half2 vn; 					vn.x = dot(i.TtoV0, normal); 					vn.y = dot(i.TtoV1, normal);  					vn = vn * 0.5 + 0.5;  					fixed4 matcapDiffuse = tex2D(_MatCapDiffuse, vn) * _DiffuseValue;		 					fixed4 matcapSpec = tex2D(_MatCapSpec, vn) * _SpecValue;	 					fixed4 finalColor = matcapDiffuse * c * _Color + matcapSpec; 					return finalColor; 				}  			ENDCG 		} 	} }

这段代码并不复杂,对比之前两章的说明很容易看懂。

系列链接(未完待续):

《Matcap Shader 详解【1】 - 基础思想与Unity中实现》

《Matcap Shader 详解【2】 - Matcap的固有色贴图与法线贴图》

《Matcap Shader 详解【3】 - 利用Matcap实现基本PBR光照模型》


――――――――这里是分割线――――――――――

С这是我最近用Matcap实现的肤质渲染(以后有可能会讲解哟):



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!