Unity制作二次元卡通渲染角色材质——5、脸部的特殊处理

news2024/10/7 16:23:01

Unity制作二次元材质角色


回到目录

大家好,我是阿赵。
这里继续讲二次元角色材质的制作。这次是讲头部的做法。

1、脸部

在这里插入图片描述

之前在分析资源的时候,其实已经发现了这个模型的脸部法线有问题,导致在做光照模型的时候,脸部很奇怪。
把fbx文件导入到3DsMax里面,可以发现
在这里插入图片描述

这个模型为了做口里面的牙齿和舌头的动画,把脸部的布线做得很极限。
然后尝试把整个脸打同一个光滑组,发现脸部的法线还是不能正常过渡,于是检查一下模型脸部的顶点,发现很多部位并没有焊接在一起。
在这里插入图片描述在这里插入图片描述

所以要解决脸部的问题,首先要先处理一下原始的模型,把该焊接的点给焊接一下。
在这里插入图片描述

进入点层级,全选所有点,然后焊接一下。
接下来给整个脸打一个光滑组
在这里插入图片描述

到这里,这个模型的脸部结构暂时来说比较正常。但由于脸部的结构特殊性,所以如果我们真的按照光照模型的NDotL去计算光影,脸部的光影在某些角度会非常的恐怖,比如这样:
在这里插入图片描述

之前我写过2篇文章去解决这个问题,各位有兴趣可以去看看:
1.使用阈值图修改角色脸部阴影
在这里插入图片描述

2.用MaxScript修改模型法线映射
在这里插入图片描述

这里我选择了第2种方法去处理。
可以看到,集中在脸部的地方,法线非常的密集,而且方向比较的杂乱。这其实是正常而且合理的,因为脸部的转折很多。但用这些法线来计算光影,效果会很奇怪。
在这里插入图片描述

使用我之前用MaxScript写的映射法线的工具,进行一下球形法线映射
在这里插入图片描述
在这里插入图片描述

现在脸部的法线就会变成很平滑的过渡,方向非常的统一。这样的法线对于脸部来说其实是不对的,但是在做光影的时候,会比较的正常。然后配合一下之前ILM贴图的G通道,把脸部的阴影减淡一些,就可以。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
这样看起来,脸部的影子就正常很多了。
这样看起来,脸部的

2、眼镜

在这里插入图片描述

如果只是赋予正常的身体材质,眼镜是这样的。
我们希望眼镜的效果大概有这些:
1.有基本颜色
2.有透明度,起码可以看到眼睛
3.有高光
4.有反射
所以我在身体材质的基础上做了一些修改:
在这里插入图片描述

1.使用正常的透明混合Blend SrcAlpha OneMinusSrcAlpha
2.使用了BlinnPhong高光
3.还是用Matcap做假反射,不过找了一个和眼镜本身颜色比较接近的Matcap反射球
完整Shader:

Shader "azhao/ToonGlass"
{
    Properties
    {
        _BaseMap ("BaseMap", 2D) = "white" {}

		_specColor("specColor",Color) = (1,1,1,1)
		_shininess("shininess", Range(1 , 100)) = 1
		_SpecAdd("SpecAdd",float) = 1.0


		_MatCapTex("MatCapTex", 2D) = "white" {}
		_MatCapIntensity("MatCapIntensity",Range(0,2)) = 1
		_MatCapPow("MatCapPow",Range(0,5)) = 1
		_MatCapUVScale("MatCapUVScale",Range(0,1)) = 1

    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
			Blend SrcAlpha OneMinusSrcAlpha
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
			#pragma multi_compile_fwdbase
			#include "AutoLight.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
				float2 uv2 : TEXCOORD1;
				float3 normal:NORMAL;
            };

            struct v2f
            {                
                float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float2 uv2 : TEXCOORD1;
				float3 worldPos :TEXCOORD2;
				float3 worldNormal :TEXCOORD3;
            };

            sampler2D _BaseMap;
            float4 _BaseMap_ST;

			float4 _specColor;
			float _shininess;

			float _SpecAdd;

			sampler2D _MatCapTex;
			float _MatCapIntensity;
			float _MatCapPow;
			float _MatCapUVScale;

			//获取HalfLambert漫反射值
			float GetHalfLambertDiffuse(float3 worldPos, float3 worldNormal)
			{
				float3 lightDir = UnityWorldSpaceLightDir(worldPos);
				float NDotL = saturate(dot(worldNormal, lightDir));
				float halfVal = NDotL * 0.5 + 0.5;
				return halfVal;
			}

			//获取BlinnPhong高光
			float GetBlinnPhongSpec(float3 worldPos, float3 worldNormal)
			{
				float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
				float3 halfDir = normalize((viewDir + _WorldSpaceLightPos0.xyz));
				float specDir = max(dot(normalize(worldNormal), halfDir), 0);
				float specVal = pow(specDir, _shininess);
				return specVal;
			}


			float2 GetMatCapUV(float3 normalWorld)
			{
				float3 normalView = mul(UNITY_MATRIX_IT_MV, normalWorld);
				return normalView.xy*0.5 + 0.5;
			}

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _BaseMap);
				o.uv2 = TRANSFORM_TEX(v.uv2, _BaseMap);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
                half4 col = tex2D(_BaseMap, i.uv);


				half specVal = GetBlinnPhongSpec(i.worldPos, i.worldNormal);
				float2 MatCapUV = GetMatCapUV(i.worldNormal)*_MatCapUVScale;
				float4 MatCapCol = tex2D(_MatCapTex, MatCapUV)*_MatCapIntensity;
				MatCapCol = pow(MatCapCol, _MatCapPow);



				half3 finalRGB = col.rgb + _specColor * specVal*_SpecAdd;
				finalRGB = finalRGB * (0.5) +MatCapCol.rgb*0.5;
				half alpha = col.a;
                return half4(finalRGB,alpha);
            }
            ENDCG
        }


    }
}

到这里,这个二次元卡通角色的材质基本上就做完了。下面说一下在这个例子里面,由于资源的原因没有做到的一些细节。虽然很遗憾不能做出效果,但也从理论上说一下应该怎样做吧。

1、头发

一般来说,头发如果有单独的问题贴图和法线贴图,比较容易做出一束一束的感觉。
可惜我这个模型素材里面,头发的贴图并没有单独的,只有一个和皮肤一样的baseMap,所以只能做到一片一片的效果。
在这里插入图片描述

然后也可以稍微从高光的角度去调整一下。
之前用于身体部分的高光的光照模型是BlinnPhong,所以光照会是一片的,我们可以试试改成Anisortropic各向异性高光。

//获取Anisortropic各向异性高光
float GetAnisortropicSpec(float3 worldPos, float3 worldNormal, float3 worldTangent)
{
	float3 binormal = cross(worldNormal, worldTangent);
	float3 lerpVal = normalize(lerp((worldNormal + binormal), binormal, _tangentVal));

	float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
	float3 halfDir = normalize((viewDir + _WorldSpaceLightPos0.xyz));
	float anistropicVal = dot(lerpVal, halfDir);
	float specDir = max(anistropicVal*anistropicVal, 0);
	float specVal = pow(specDir, _shininess);
	return specVal;
}

在这里插入图片描述

各向异性的高光一般是用于做一条一条形状的高光,不过由于我这个模型实在有点过于光滑,也没有法线贴图,所以这种感觉不是很明显。

2、眼睛

为了方便观察,先把眼镜隐藏了
之前我们做内描线的时候,其实发现了一个特别的东西,就是这个眼睛的贴图,居然不是在baseMap,而是在ILM贴图的A通道
在这里插入图片描述
在这里插入图片描述

在这个眼球或者瞳孔的地方,我虽然加上了高光和Matcap,但发现眼睛他其实并没有什么效果。这是因为,这里的瞳孔其实只是2个平面,在平面上面,不论是高光还是Matcap都不可能出现眼球的球形高光变化。
这个时候,要么把眼球改成真的球形,要么,使用法线贴图。
如果有法线贴图。法线贴图的做法是先采样一个凹进去的部分,然后给他赋予颜色,再把法系翻转,采样一个凸出来的部分,在凸出来的部分上面做高光和Matcap。
由于我手上这个模型并没有提供法线贴图,所以这部分我暂时就演示不了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/621935.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阅读ConcurrentHashMap源码,我学到了什么?

文章目录 ConcurrentHashMap怎样保证线程安全的put元素的流程具体对于红黑树是怎样保证线程安全的如何并发安全的初始化一个数组如何统计存储元素个数的怎样进行多线程扩容的 首先说明, 本篇分析基于jdk1.8. ConcurrentHashMap怎样保证线程安全的 ConcurrentHashMap主要是通过…

Java:字符流

字符流的底层其实就是字节流。 字符流字节流字符集 结构体系: 1.特点 输入流:一次读一个字节,遇到中文时,一次读多个字节。 输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中。 2.使用场景 对于纯文本…

AI实战营第二期 笔记5——MMPretrain代码课

文章目录 摘要MMPreTrain实战安装推理 OR 使用API数据集训练与测试微调 摘要 MMPretrain 是一个全新升级的预训练开源算法框架,旨在提供各种强大的预训练主干网络, 并支持了不同的预训练策略。MMPretrain 源自著名的开源项目 MMClassification 和 MMSel…

chatgpt赋能python:Python开立方函数math:让数学计算更加简单

Python开立方函数math:让数学计算更加简单 Python作为一种高级编程语言,提供了丰富的数学计算功能,其中就包括了开立方函数math。本篇文章将详细介绍math开立方函数在Python中的使用方法及其优点。 什么是开立方函数? 开立方函…

chatgpt赋能python:Python开发BI,助力企业数据驱动决策

Python开发BI,助力企业数据驱动决策 随着企业数据规模的呈现爆炸式增长,传统的Excel等表格计算工具已经无法满足业务需求,因此,企业需要更加全面和强大的方法来进行数据分析和展示。此时,BI(全称Business …

css蓝桥杯--电影院排座位

目录 一、介绍二、准备三、⽬标四、代码五、知识点六、完成 一、介绍 随着⼈们⽣活⽔平的⽇益提升,电影院成为了越来越多的⼈休闲娱乐,周末放松的好去处。各个城市的电影院数量也随着市场的需求逐年攀升。近⽇,⼜有⼀个电影院正在做着开张前…

chatgpt赋能python:Python强制关闭程序的解决方案

Python 强制关闭程序的解决方案 在Python开发过程中,难免会遇到程序卡死,无响应等问题,这时候如果无法正常退出程序,就需要进行强制关闭。本篇文章将介绍Python强制关闭程序的几种解决方案。 方法一: 使用系统命令 在Linux或Ma…

【Java】JavaWEB核心要点总结:64

文章目录 1. TCP 和 UDP的异同2. TCP为什么要三次握手 两次不行吗3. get post put 请求方式有什么区别4. 什么是XXS攻击 如何避免5. 什么是 CSRF 攻击,如何避免 1. TCP 和 UDP的异同 TCP(Transmission Control Protocol)和UDP(Use…

typedef 和 # define 用法区别

typedef 和 # define 用法区别 前言1. 原理不同1.1 typedef int * int_ptr;与#define int_ptr int * 详细讲解 2. 功能不同3. 作用域不同 前言 博主在牛客网上看到了一道有关typedef和# define题目。发现有很多初学的小伙伴对两者的用法不是特别清楚,所以博主在这总…

python内存

在python中,一切都是对象。Python从设计之初就是一门面向对象的语言,它有一个重要的概念,即一切皆对象。 Java虽然也是面向对象编程的语言,但是血统没有Python纯正。比如Java的八种基本数据类型之一int,在持久化的时候…

HCIA-NAT

目录 NAT:网络地址转换 NAT原理: NAT转换原理图: 静态NAT 静态NAT的工作原理: 静态NAT配置命令 静态NAT配置实例: 动态NAT 动态NAT的工作原理 动态NAT: 动态NAT配置命令 动态NAT案例 NAPT NA…

css蓝桥杯--⾃适应⻚⾯

目录 一、介绍二、准备三、⽬标四、代码五、完成 一、介绍 响应式布局是在 2010 年 5 ⽉份提出的⼀个概念,这个概念是为解决移动互联⽹浏览⽽诞⽣的。简⽽⾔之,就是⼀个⽹站能够兼容多个终端——⽽不是为每个终端做⼀个特定的版本。通过响应式布局可以为…

EXCEL文本处理总结:如何查找(/定位)字符串内,符合条件的多个符号里的最后一个?

目录 题外话:学习总结 1 新手切忌贪多 2 熟练者切忌懒惰 3 这2件事恰恰都和人性相反 1 EXCEL文本处理相关函数 2 查找函数 find() 和 search() 2.1 find() 2.2 search() 2.3 下面是测试的公式情况 3 如何查找(定位)符合条件的某个字符的位置?…

MySQL触发器的使用

目录 一、前言二、触发器分类1.插入触发器2.更新触发器3.删除触发器 三、查看触发器四、异常处理五、小结 一、前言 各种主流数据库,都集成了触发器的功能。触发器提供了一种机制,允许开发者在对数据库表的插入、更新、删除的前后捕获相应的数据行。从而针对数据行实现特定的逻…

DebugView的使用

目录 一、前言二、本机调试1.DebugView程序文件说明2.OutputDebugString函数使用3.示例程序4.远程调试 三、问题与注意事项四、小结 一、前言 DebugView是windows下的一款调试工具,可以捕获程序输出的日志,分为64位和32位,支持应用层和内核层的日志捕获,利用它排除bug是个不错的…

chatgpt赋能python:Python开发手机软件的优势和挑战

Python开发手机软件的优势和挑战 随着智能手机的普及,移动应用开发已成为当前最热门的技术领域之一。随着越来越多的企业意识到移动应用的重要性,越来越多的开发者开始加入这个领域。 在移动应用的开发中,由于其高效性和易于学习的特点&…

【Flutter】Dart/Flutter SDK如何降低版本、回退到指定版本

因为dart3.0以后不再支持 no-sound-null-safety;但是有些项目不得以切换到dart3.0以前继续使用运行项目 方法1: 通过 $ flutter downgrade命令,将flutter降级为当前通道的上一个活动版本; 如果没有存在老版本则会提示 flutter …

从零手写操作系统之RVOS软件定时器实现-08

从零手写操作系统之RVOS软件定时器实现-08 定时器分类软件定时器的分类软件定时器设计与实现软件定时器调用流程增加对周期性定时任务支持测试优化点 本系列参考: 学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春 整理而来,主要作为xv6操作系统学习的一个前置基础。…

chatgpt赋能python:Python强制等待:如何优化你的Python技能

Python强制等待:如何优化你的Python技能 在Python编程中,强制等待是一种非常重要的程序设计方式。Python代码中的强制等待通常使用time.sleep()方法实现。在本文中,我们将详细介绍什么是Python强制等待,以及如何使用它来优化你的…

基于最近电平逼近的开环MMC逆变器MATLAB仿真模型

资源地址: 模型介绍: MATLAB21b版本 DC:12kV,N=12, 采用最近电平逼近调制,采用基于排序的均压方法,冒泡排序+桥臂电流方向判断。 连接负载,可以得到13电平相电压波形。…