文章目录
-
- [0. 效果预览](#0. 效果预览)
- [1. 原理简述](#1. 原理简述)
- [2. 功能点](#2. 功能点)
- [3. 完整 Shader(可直接用)](#3. 完整 Shader(可直接用))
- [4. 使用方法](#4. 使用方法)
- [5. 参数说明](#5. 参数说明)
- [6. 变体与扩展](#6. 变体与扩展)
-
- [6.1 法线贴图增强 Matcap 细节](#6.1 法线贴图增强 Matcap 细节)
- [6.2 双层 Matcap(漫反射 + 高光)](#6.2 双层 Matcap(漫反射 + 高光))
- [6.3 Matcap + 环境光遮蔽](#6.3 Matcap + 环境光遮蔽)
- [7. 常见问题](#7. 常见问题)
- [8. 性能建议](#8. 性能建议)
0. 效果预览

Matcap(Material Capture,材质捕捉)是一种用一张球形光照贴图伪造任意材质效果的技巧:金属高光、玉石通透、卡通光泽、X 光效果------换张贴图就换一种材质,不需要真正的光照计算。原理极简、性能极低、效果极好,是角色展示、雕刻预览、风格化渲染的利器。
1. 原理简述
Matcap 的本质:把法线从世界空间转到视图空间,取 XY 分量映射到 0~1 作为 UV,去采样一张预烘焙的球形光照贴图。
关键公式:
hlsl
float3 normalVS = mul((float3x3)UNITY_MATRIX_V, normalWS); // 世界法线 → 视图空间法线
float2 matcapUV = normalVS.xy * 0.5 + 0.5; // 映射到 0~1 作为 UV
half3 matcapColor = tex2D(_MatcapTex, matcapUV).rgb; // 采样 Matcap 贴图
为什么 view-space normal 的 XY 能当 UV?
视图空间中,摄像机看向 -Z 方向。法线的 X 分量 = 左右偏转,Y 分量 = 上下偏转。一个球体在视图空间中,法线的 XY 刚好覆盖 -1~1 的圆形区域------这正好对应一张圆形 Matcap 贴图的所有位置。
所以:球体上每个朝向不同的面,都会采样到 Matcap 贴图上不同位置的颜色,完美还原贴图中预烘焙的光照信息。
Matcap 贴图长什么样?
就是一张对着球体拍摄(或渲染)的正方形照片,球体上已经包含了完整的材质光照信息。网上搜 "matcap texture" 有大量免费资源。
2. 功能点
- Matcap 材质采样:视图空间法线 XY → UV,采样球形光照贴图
- 主贴图叠乘:保留模型原始纹理细节,Matcap 控制光照/材质感
- 混合强度可调 :
_MatcapStrength控制 Matcap 和基础色的混合比例 - Matcap 贴图可热替换:Inspector 里换一张球形图就换一种材质风格
- 加法/乘法混合可选:加法模式叠加高光,乘法模式替换光照
- GPU Instancing:支持多实例渲染
3. 完整 Shader(可直接用)
hlsl
Shader "Custom/Matcap_URP"
{
Properties
{
// 主贴图(模型漫反射纹理)
_BaseMap ("Base Map", 2D) = "white" {}
// 主颜色叠乘
_BaseColor ("Base Color", Color) = (1,1,1,1)
// Matcap 球形光照贴图
_MatcapTex ("Matcap Texture", 2D) = "white" {}
// Matcap 影响强度(0=纯基础色,1=完全 Matcap)
_MatcapStrength ("Matcap Strength", Range(0, 1)) = 1.0
// 混合模式:0=乘法(替换光照),1=加法(叠加高光)
_MatcapBlendMode ("Blend Mode (0=Multiply, 1=Add)", Range(0, 1)) = 0.0
}
SubShader
{
Tags
{
"RenderPipeline" = "UniversalRenderPipeline"
"Queue" = "Geometry"
"RenderType" = "Opaque"
}
Pass
{
Name "MatcapPass"
Tags { "LightMode" = "UniversalForward" }
Cull Back
ZWrite On
Blend Off
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
// GPU Instancing 支持
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
// =========================================================
// 贴图声明
// =========================================================
TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);
TEXTURE2D(_MatcapTex); SAMPLER(sampler_MatcapTex);
// =========================================================
// 材质属性(与 Properties 一一对应)
// =========================================================
float4 _BaseMap_ST;
float4 _BaseColor;
float _MatcapStrength;
float _MatcapBlendMode;
struct Attributes
{
float4 positionOS : POSITION; // 模型空间顶点
float3 normalOS : NORMAL; // 模型空间法线
float2 uv : TEXCOORD0; // UV 坐标
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionHCS : SV_POSITION; // 裁剪空间位置
float2 uv : TEXCOORD0; // 传递 UV
float2 matcapUV : TEXCOORD1; // Matcap 采样 UV
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
// =========================================================
// 顶点着色器:计算 Matcap UV
// =========================================================
Varyings vert(Attributes v)
{
Varyings o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
// 模型空间 → 裁剪空间
o.positionHCS = TransformObjectToHClip(v.positionOS.xyz);
// UV 变换
o.uv = TRANSFORM_TEX(v.uv, _BaseMap);
// ===== 核心:计算 Matcap UV =====
// 1) 模型空间法线 → 世界空间法线
float3 normalWS = TransformObjectToWorldNormal(v.normalOS);
// 2) 世界空间法线 → 视图空间法线
float3 normalVS = mul((float3x3)UNITY_MATRIX_V, normalWS);
// 3) 取 XY 分量,映射 -1~1 → 0~1 作为 Matcap UV
o.matcapUV = normalVS.xy * 0.5 + 0.5;
return o;
}
// =========================================================
// 片元着色器:采样 Matcap 并混合
// =========================================================
half4 frag(Varyings i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
// 1) 采样主贴图
half4 baseCol = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, i.uv);
baseCol *= (half4)_BaseColor;
// 2) 采样 Matcap 贴图
half3 matcap = SAMPLE_TEXTURE2D(_MatcapTex, sampler_MatcapTex, i.matcapUV).rgb;
// 3) 混合:乘法模式 vs 加法模式
half3 mulResult = baseCol.rgb * matcap; // 乘法:Matcap 替换光照
half3 addResult = baseCol.rgb + matcap; // 加法:Matcap 叠加高光
half3 blended = lerp(mulResult, addResult, _MatcapBlendMode);
// 4) 按强度混合回基础色
half3 finalColor = lerp(baseCol.rgb, blended, _MatcapStrength);
return half4(finalColor, baseCol.a);
}
ENDHLSL
}
}
}
4. 使用方法
-
在 Unity 项目的
Assets/Shaders/下新建文件Matcap_URP.shader,粘贴上方完整代码。 -
新建材质(Create → Material),Shader 选择
Custom/Matcap_URP。 -
准备 Matcap 贴图:
- 网上搜索 "matcap texture free" 下载(推荐 256×256 或 512×512)
- 常用资源站:matcaps GitHub 合集有 600+ 张免费 Matcap
- 也可以自己在 Blender/Unity 中对着一个球体截图,手动制作
-
Matcap 贴图的 Import Settings:
Wrap Mode→ Clamp(防止边缘采样到对面)Filter Mode→ Bilinear(平滑过渡)- 关闭
Generate Mip Maps(可选,关掉更锐利)
-
将材质赋给场景中的模型(角色、雕塑、球体等曲面模型效果最好)。
-
在 Inspector 中配置参数:
Matcap Texture:拖入 Matcap 贴图Matcap Strength:1.0 = 完全 Matcap 效果Blend Mode:0 = 乘法(Matcap 当光照用),1 = 加法(Matcap 当高光叠加)


- 旋转模型或摄像机,观察 Matcap 效果------材质"粘"在模型上,不随旋转改变光照方向,这是 Matcap 的核心特征。

5. 参数说明
| 参数 | 类型 | 范围/默认值 | 说明 |
|---|---|---|---|
_BaseMap |
2D | white | 模型主贴图(漫反射纹理) |
_BaseColor |
Color | (1,1,1,1) | 主颜色叠乘 |
_MatcapTex |
2D | white | Matcap 球形光照贴图 |
_MatcapStrength |
Range(0,1) | 1.0 | Matcap 影响强度:0=纯基础色,1=完全 Matcap |
_MatcapBlendMode |
Range(0,1) | 0.0 | 混合模式:0=乘法(替换光照),1=加法(叠加高光) |
6. 变体与扩展
6.1 法线贴图增强 Matcap 细节
用法线贴图扰动视图空间法线,让 Matcap 跟随表面凹凸细节变化:
hlsl
// 需要在 Attributes 中加 tangentOS
float4 tangentOS : TANGENT;
// vert 中传递 TBN 矩阵到 frag
float3 tangentWS = TransformObjectToWorldDir(v.tangentOS.xyz);
float3 bitangentWS = cross(normalWS, tangentWS) * v.tangentOS.w;
// frag 中采样法线贴图并转到视图空间
float3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, i.uv));
float3 perturbedWS = normalize(
normalTS.x * tangentWS + normalTS.y * bitangentWS + normalTS.z * normalWS
);
float3 normalVS = mul((float3x3)UNITY_MATRIX_V, perturbedWS);
float2 matcapUV = normalVS.xy * 0.5 + 0.5;
这样 Matcap 的高光和阴影会跟随法线贴图的凹凸走,细节大幅提升。
6.2 双层 Matcap(漫反射 + 高光)
用两张 Matcap 分别控制漫反射和高光,模拟更真实的材质:
hlsl
// Properties 中加第二张 Matcap
_MatcapSpecTex ("Matcap Specular", 2D) = "black" {}
// frag 中分别采样
half3 matcapDiffuse = SAMPLE_TEXTURE2D(_MatcapTex, sampler_MatcapTex, matcapUV).rgb;
half3 matcapSpec = SAMPLE_TEXTURE2D(_MatcapSpecTex, sampler_MatcapSpecTex, matcapUV).rgb;
half3 finalColor = baseCol.rgb * matcapDiffuse + matcapSpec;
漫反射 Matcap 控制整体色调,高光 Matcap 叠加光泽点,两层分离更灵活。
6.3 Matcap + 环境光遮蔽
用顶点色或 AO 贴图调制 Matcap 强度,让缝隙/凹陷处更暗:
hlsl
// 采样 AO 贴图
half ao = SAMPLE_TEXTURE2D(_AOMap, sampler_AOMap, i.uv).r;
// AO 调制 Matcap
half3 finalColor = baseCol.rgb * matcap * ao;
7. 常见问题
Q: Matcap 效果随摄像机旋转改变了,不是"粘"在模型上?
A: 这是正常的------Matcap 本质是基于视角的,摄像机动了视图空间法线就变了。这正是 Matcap 的特性:同一个面朝向摄像机时是亮的,转过去就暗了。如果需要固定光照方向,应该用传统光照模型而非 Matcap。
Q: 平面(Plane/Quad)上看到的 Matcap 是一个纯色?
A: 平面的法线方向一致,所有顶点的视图空间法线 XY 相同,采样到 Matcap 的同一个位置。Matcap 需要法线方向有变化的曲面模型才能展现效果。
Q: Matcap 边缘有明显的截断/接缝?
A: 检查贴图 Wrap Mode 是否设为 Clamp。如果是 Repeat,边缘像素会采样到对面的颜色。另外确保 Matcap 贴图的圆形区域外(四个角)是统一颜色。
Q: 模型上有硬边/接缝处 Matcap 不连续?
A: 硬边处法线突变,Matcap UV 也会突变。用 平滑法线 可以消除硬边(在建模软件中设置 Smooth Shading)。或者在 frag 中计算 Matcap UV(而非 vert),插值更平滑。
Q: Matcap 贴图从哪里找?
A: GitHub 上的 nidorx/matcaps 有 600+ 张免费 Matcap 贴图,覆盖金属、塑料、皮肤、玉石、卡通等各种材质风格。ZBrush 和 Blender 也自带很多 Matcap。
8. 性能建议
- 极低开销:整个 Matcap 效果只需要一个矩阵乘法(法线变换)和一次贴图采样,是性能最友好的材质方案之一。
- 无光照计算 :Matcap 不依赖场景灯光,不需要
GetMainLight()、不算 NdotL,完全绕过光照管线。移动端大量角色场景特别适合。 - 贴图尺寸小:Matcap 贴图 256×256 已经足够大多数场景,512×512 适合特写。比 PBR 的 albedo + normal + metallic + roughness 省一大堆带宽。
- 合批友好:单 Pass Opaque Shader,支持 SRP Batcher 和 GPU Instancing。不同 Matcap 贴图需要不同材质,会打断合批------如果需要合批,可以把多张 Matcap 打成图集。
- 不适合动态光照:Matcap 是预烘焙的固定光照环境,不会响应场景灯光变化。如果需要动态光照 + Matcap 风格,考虑用 Ramp Shading(文章 24)代替。