当前位置: 首页 > news >正文

Matcap Shader 详解【4】 - 多材质与混合材质

Matcap Shader 详解【4】 - 多材质与混合材质

https://zhuanlan.zhihu.com/p/38575505

 

图形程序/技术美术 Shading Artist 小屋

首发于图形程序/技术美术 Shading Artist 小屋

Matcap Shader 详解【4】 - 多材质与混合材质

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

shader

游戏开发

Unity(游戏引擎)

文章被以下专栏收录

图形程序/技术美术 Shading Artist 小屋

 

图形程序/技术美术 Shading Artist 小屋

Shading Artist / TA 交流地

进入专栏

推荐阅读

在Unity中使用Amplify制作雨天特效Shader(第二部分)

在Unity中使用Amplify制作雨天特效Shader(第二部分)

Faith...发表于3D Ar...

写一个水材质与冰材质的shader

写一个水材质与冰材质的shader

雪落桑田

【翻译】通过Unity学习PBR算法 ⑥

【翻译】通过Unity学习PBR算法 ⑥

董宸发表于游戏开发我...

Mask的艺术 – 近似PBR的Dota2角色渲染

Mask的艺术 – 近似PBR的Dota2角色渲染

拳四郎发表于游戏开发启...

5 条评论

写下你的评论...

 

  • 知乎用户知乎用户7 个月前

    用CUBE就能转了,IBL基本思想,最后还是看美术

天源

天源 (作者) 回复知乎用户6 个月前

可以看一下最新一篇Matcap Shader 详解【5】-动态光照、机位与反射,讲了讲Cubemap的优缺点和另外一种思路

  • 星临

    星临7 个月前

    可以参考一下dota2的实现 Mask的艺术 – 近似PBR的Dota2角色渲染,思路是去掉matcap图的高光信息, 用来模拟反射。

天源

天源 (作者) 回复星临6 个月前

嗯嗯没错~可以看一下最新一篇

  • 知乎用户知乎用户7 个月前

    收藏了

 

相关文章:

  • 一道技术美术的面试题
  • 塞尔达风之杖技术分析-角色渲染和面部表情
  • 关于 Unreal4 技美:如果你希望往UE技美方向发展。。。。
  • [UE4]性能优化指南(美术向)
  • 【多图】【严肃的技术帖】用UE4做黑丝材质
  • UE4静态网格模型距离场DistanceFields美术应用
  • Rendering in UE4(Epic Game TA-Homam Bahnassi讲座个人笔记)
  • Unity3D研究院编辑器之ManagedStaticReferences()静态引用(二十九)
  • 总结《Ray Tracing from the Ground Up》
  • Sunflow
  • Mastering Unity Project Folder Structure. Level 1 – Reserved Folders, Unity 各个文件夹介绍
  • Unity 资源释放
  • UE4 Hair Shading
  • Gameplay Ability System
  • 由UE4一个离奇的崩溃探究 UObjects GC的实现方式
  • (三)从jvm层面了解线程的启动和停止
  • 2018一半小结一波
  • ES10 特性的完整指南
  • HashMap ConcurrentHashMap
  • Magento 1.x 中文订单打印乱码
  • Netty源码解析1-Buffer
  • nginx 负载服务器优化
  • NSTimer学习笔记
  • PAT A1017 优先队列
  • Python学习之路16-使用API
  • Redis的resp协议
  • windows下使用nginx调试简介
  • 编写高质量JavaScript代码之并发
  • 从零开始的无人驾驶 1
  • 从伪并行的 Python 多线程说起
  • 大整数乘法-表格法
  • 日剧·日综资源集合(建议收藏)
  • 如何解决微信端直接跳WAP端
  • 实现菜单下拉伸展折叠效果demo
  • 追踪解析 FutureTask 源码
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • Hibernate主键生成策略及选择
  • MPAndroidChart 教程:Y轴 YAxis
  • postgresql行列转换函数
  • ​【已解决】npm install​卡主不动的情况
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • (1)(1.19) TeraRanger One/EVO测距仪
  • (13)Hive调优——动态分区导致的小文件问题
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (HAL库版)freeRTOS移植STMF103
  • (Java)【深基9.例1】选举学生会
  • (二)JAVA使用POI操作excel
  • ****Linux下Mysql的安装和配置
  • *上位机的定义
  • .NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
  • .NET 药厂业务系统 CPU爆高分析
  • .Net 中Partitioner static与dynamic的性能对比
  • .Net中wcf服务生成及调用