【Unity Shader URP】Matcap 材质捕捉实战教程

文章目录

    • [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. 使用方法

  1. 在 Unity 项目的 Assets/Shaders/ 下新建文件 Matcap_URP.shader,粘贴上方完整代码。

  2. 新建材质(Create → Material),Shader 选择 Custom/Matcap_URP

  3. 准备 Matcap 贴图:

    • 网上搜索 "matcap texture free" 下载(推荐 256×256 或 512×512)
    • 常用资源站:matcaps GitHub 合集有 600+ 张免费 Matcap
    • 也可以自己在 Blender/Unity 中对着一个球体截图,手动制作
  4. Matcap 贴图的 Import Settings

    • Wrap ModeClamp(防止边缘采样到对面)
    • Filter ModeBilinear(平滑过渡)
    • 关闭 Generate Mip Maps(可选,关掉更锐利)
  5. 将材质赋给场景中的模型(角色、雕塑、球体等曲面模型效果最好)。

  6. 在 Inspector 中配置参数:

    • Matcap Texture:拖入 Matcap 贴图
    • Matcap Strength:1.0 = 完全 Matcap 效果
    • Blend Mode:0 = 乘法(Matcap 当光照用),1 = 加法(Matcap 当高光叠加)


  1. 旋转模型或摄像机,观察 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)代替。
相关推荐
深蓝海拓2 小时前
基于QtPy (PySide6) 的PLC-HMI工程项目(十)框架初成的阶段总结
网络·笔记·python·学习·ui·plc
魔士于安2 小时前
unity urp材质球大全
游戏·unity·游戏引擎·材质·贴图·模型
Swift社区2 小时前
鸿蒙游戏 UI 怎么设计才不乱?
游戏·ui·harmonyos
那个失眠的夜2 小时前
AspectJ
java·开发语言·数据库·spring
杨凯凡2 小时前
【014】基本类型与包装类:缓存、相等性、NPE
java·数据结构·缓存
emmjng3693 小时前
使用飞算JavaAI实现在线图书借阅平台
java
CoderYanger3 小时前
14届蓝桥杯省赛Java A 组Q1~Q3
java·开发语言·线性代数·算法·职场和发展·蓝桥杯
钮钴禄·爱因斯晨3 小时前
他到底喜欢我吗?赛博塔罗Java+前端实现,一键解答!
java·开发语言·前端·javascript·css·html
词元Max3 小时前
Java 转 AI Agent 开发学习路线(2026年3月最新版)
java·人工智能·学习