Matcap Shader 详解【4】 - 多材质与混合材质
Matcap Shader 详解【4】 - 多材质与混合材质
https://zhuanlan.zhihu.com/p/38575505
首发于图形程序/技术美术 Shading Artist 小屋
Matcap Shader 详解【4】 - 多材质与混合材质
天是天光云影共徘徊的天,源是为有源头活水来的源
前言
上篇中讲了一下怎样用Matcap实现PBR光照模型,但实践中还会遇到一个新的问题:
- BPR材质里可以实现多种材质混合的效果,比如一把斧头拥有金属和木质,而一件衣服可能同时具有布料和皮质。
这次就来讨论一下多材质实现的两种方案吧~
Unity官方PBR多材质效果
这次使用了一个中世纪骑士的模型,所有的质感都是通过高光贴图展现的——比如身上同时具备金属和皮革,在这张图上,白色代表了金属部分,黑色代表非金属,灰色则是介于两者之间:
Unity官方PBR(Standard Specular Setup)渲染结果如下:
方法一:通过高光贴图实现Matcap材质混合
高光贴图作为蒙版,将不该添加高光的部分遮掩掉,便可以实现混合的效果:
源码如下:
Shader "TJia/Matcap_SpecTex" {
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,5)) = 0
_SpecTex("Spec Tex", 2D) = "white" {}
_SpecTexValue("Spec Tex Value", Range(0,2)) = 1
}
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 sampler2D _SpecTex;
uniform fixed _BumpValue;
uniform fixed _DiffuseValue;
uniform fixed _SpecValue;
uniform fixed _SpecTexValue;
fixed lum(fixed3 col)
{
return col.r * 0.2 + col.g * 0.7 + col.b * 0.1;
}
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 specTex = tex2D(_SpecTex, i.uv.xy) * _SpecTexValue;
fixed4 matcapSpec = tex2D(_MatCapSpec, vn) * _SpecValue * specTex;
fixed4 diffuse = matcapDiffuse * c * _Color;
fixed4 finalColor = diffuse + lerp(0, matcapSpec, lum(specTex.rgb));
return finalColor;
}
ENDCG
}
}
}
说明:
- 高光值与高光贴图相乘,是对高光的第一次遮罩
- 最终与漫反射颜色叠加时,高光又通过高光贴图的明度做了一次插值,这是第二次遮罩
方法二:通过混合贴图实现Matcap材质混合
上述方法中,高光贴图的作用是遮罩,而另一种常见的材质混合方法便是使用混合贴图——比如Unity地形系统中的贴图混合,就是通过这样的方式。
所以这一次,我们将高光贴图当做混合贴图来使用,从而混合两种不同的Matcap材质——白色用材质一,黑色用材质二,灰色介于两者之间:
源码:
Shader "TJia/Matcap_PBR_Mask" {
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,5)) = 0
_MatCapDiffuse2("MatCap Diffuse2 (RGB)", 2D) = "white" {}
_DiffuseValue2("Diffuse Value2", Range(0,5)) = 1
_MatCapSpec2("MatCap Spec2 (RGB)", 2D) = "white" {}
_SpecValue2("Spec Value2", Range(0,5)) = 0
_MatcapMask("Matcap Mask", 2D) = "white" {}
_MatcapMaskValue("Matcap Mask Value", Range(0,1)) = 0
}
Subshader {
Tags { "RenderType"="Opaque" }
Pass {
Tags { "LightMode" = "Always" }
Cull Off
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 _MatCapSpec;
uniform sampler2D _MatCapDiffuse2;
uniform sampler2D _MatCapSpec2;
uniform sampler2D _MainTex;
uniform sampler2D _MatcapMask;
uniform fixed _BumpValue;
uniform fixed _DiffuseValue;
uniform fixed _SpecValue;
uniform fixed _DiffuseValue2;
uniform fixed _SpecValue2;
uniform fixed _MatcapMaskValue;
fixed lum(fixed3 col)
{
return col.r * 0.2 + col.g * 0.7 + col.b * 0.1;
}
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 mask = tex2D(_MatcapMask, i.uv.xy) * _MatcapMaskValue;
fixed4 matcapDiffuse = lerp(tex2D(_MatCapDiffuse, vn) * _DiffuseValue, tex2D(_MatCapDiffuse2, vn) * _DiffuseValue2, mask);
fixed4 matcapSpec = lerp(tex2D(_MatCapSpec, vn) * _SpecValue, tex2D(_MatCapSpec2, vn) * _SpecValue2, mask * lum(mask));
fixed4 diffuse = matcapDiffuse * c * _Color;
fixed4 finalColor = diffuse + matcapSpec;
return finalColor;
}
ENDCG
}
}
}
说明:
- 漫反射颜色通过混合贴图差值获得(使用了rgba通道)
- 高光颜色通过混合贴图的明度 差值获得
一些趣味玩法
一般情况下,两个Matcap材质混合足够表达一个物体了,如果不够,可以使用通道混合的方法,用RGBA的0和1代表8种不同的材质,实现更多的混合。(需要注意的是,OpenGLES2最多只支持八张贴图,不过聪明的你们一定可以找到解决方法的~比如将16张Matcap采样图放入同一张贴图等等)
除此之外呢,Matcap实现的效果依旧是充满了不同的玩法,比如快速更换不同感觉的金属:
或者变身成壕,穿上玻璃种翡翠之衣,戴上粉水晶之甲:
附上一点Matcap渲染的细节~:
系列链接(全):
Matcap Shader 详解【1】 - 基础思想与Unity中实现
Matcap Shader 详解【2】 - Matcap的固有色贴图与法线贴图
Matcap Shader 详解【3】 - 利用Matcap实现基本PBR光照模型
Matcap Shader 详解【4】 - 多材质与混合材质
Matcap Shader 详解【5】-动态光照、机位与反射
Matcap Shader 详解【6】-平面渲染与更佳的反射
编辑于 2018-07-19
文章被以下专栏收录
Shading Artist / TA 交流地
推荐阅读
在Unity中使用Amplify制作雨天特效Shader(第二部分)
写一个水材质与冰材质的shader
【翻译】通过Unity学习PBR算法 ⑥
Mask的艺术 – 近似PBR的Dota2角色渲染
5 条评论
写下你的评论...
-
知乎用户7 个月前
用CUBE就能转了,IBL基本思想,最后还是看美术
天源 (作者) 回复知乎用户6 个月前
可以看一下最新一篇Matcap Shader 详解【5】-动态光照、机位与反射,讲了讲Cubemap的优缺点和另外一种思路
-
星临7 个月前
可以参考一下dota2的实现 Mask的艺术 – 近似PBR的Dota2角色渲染,思路是去掉matcap图的高光信息, 用来模拟反射。
嗯嗯没错~可以看一下最新一篇
-
知乎用户7 个月前
收藏了