Unity制作二次元卡通渲染角色材质——3、高光反射与ILM贴图

news2024/11/26 16:31:50

Unity制作二次元材质角色


回到目录

大家好,我是阿赵。
这里继续来讲二次元角色的材质。上次讲了光影的色阶化问题,这次继续讲光照模型效果的问题。
之前我们说过,光照模型的最后效果是:
环境色+漫反射+高光+反射。
这里我们可以先忽略环境光,然后之前做了漫反射,用的是HalfLambert,剩下的就是高光和反射了

一、高光

继续套用之前学过的高光光照模型,习惯上是使用BlinnPhong。

//获取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;
}
half4 frag (v2f i) : SV_Target
{
    half4 col = tex2D(_BaseMap, i.uv);
	half4 sssCol = tex2D(_SSSMap, i.uv);

	//色阶化
	half halfLambert = GetHalfLambertDiffuse(i.worldPos, i.worldNormal);
	half toonVal = smoothstep(_GradationMin, _GradationMax, halfLambert);
	//高光
	half specVal = GetBlinnPhongSpec(i.worldPos, i.worldNormal);	
	half3 finalRGB = col.rgb*toonVal + sssCol * (1 - toonVal) + _specColor * specVal;
	half alpha = col.a;
    return half4(finalRGB,alpha);
}

在这里插入图片描述

加上高光之后,模型就变成这样了,高光有点强过头了。先不管,把效果都加上,最后再调。

二、反射

熟悉我的朋友都知道了,加反射效果,阿赵一般是推荐Matcap的,特别是这种不需要真反射,只是要一点环境假反射的情况。
于是又找到Matcap的代码,加上去。

float2 GetMatCapUV(float3 normalWorld)
{
	float3 normalView = mul(UNITY_MATRIX_IT_MV, normalWorld);
	return normalView.xy*0.5 + 0.5;
}
half4 frag (v2f i) : SV_Target
{
    // sample the texture
    half4 col = tex2D(_BaseMap, i.uv);
	half4 sssCol = tex2D(_SSSMap, i.uv);

	//色阶化
	half halfLambert = GetHalfLambertDiffuse(i.worldPos, i.worldNormal);
	half toonVal = smoothstep(_GradationMin, _GradationMax, halfLambert);

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

	half3 finalRGB = col.rgb*toonVal + sssCol  * (1 - toonVal)+_specColor* specVal;
	finalRGB = finalRGB * MatCapCol.rgb;

	half alpha = col.a;
    return half4(finalRGB,alpha);
}

在这里插入图片描述

这个效果,已经加上了BlinnPhong高光、Matcap反射。但明显效果是不对的,角色像一个少林寺十八铜人似的。

三、ILM贴图

上面把高光和反射加上去之后,发现效果不对。那么需要怎样去修正呢?
我们可以分析一下,效果不对的原因是什么:
1、某些部位的反射强度不对,比如皮肤,不应该反射这么强烈
2、高光的范围不对,有些部位的高光范围应该被控制在一个局部
3、反射的范围不对,反射不应该所有地方都一样的强度,应该根据实际的材质来显示,比如皮肤是不会反射的,皮衣可能有一点,吉他的金属漆部分应该有比较强的反射。
那么,我们应该怎样去控制这些强度和范围呢?这里我们需要回顾一下,之前分析资源时,得到的ILM贴图的实际情况:
由于已经知道ILM贴图的A通道是内描线,所以这里先忽略,只看RGB三个通道:
首先是R通道:
在这里插入图片描述

然后是G通道:
在这里插入图片描述

然后是B通道:
在这里插入图片描述

这里可以猜测一下,三个通道的作用了。
比较明显的是R通道,他明显是控制高光强弱的。
G通道的作用不是很明显,只有脸部和身上某些部位是比较白,其他的颜色都一样。这样应该是控制影子强度的,
B通道只有某些局部亮起来,大部分都是黑的,可以理解成,是用来控制高光和反射的形状范围的。
于是我们可以把ILM贴图对应的通道的值,加在之前的高光和反射上面。
首先来看高光部分:

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

	//色阶化
	half halfLambert = GetHalfLambertDiffuse(i.worldPos, i.worldNormal);
	half toonVal = smoothstep(_GradationMin, _GradationMax, halfLambert);
	//高光
	half specVal = GetBlinnPhongSpec(i.worldPos, i.worldNormal);
	half3 finalRGB = col.rgb*toonVal + sssCol  * (1 - toonVal)+_specColor* specVal*ilmCol.r+ _specColor * specVal*ilmCol.b*_SpecAdd;		

	half alpha = col.a;
    return half4(finalRGB,alpha);
}

使用ILM贴图的R通道控制高光强度,本来ILM贴图的B通道是控制形状的,我这里修改了一下用法,变成了在R通道控制完强度之后,使用B通道把某些区域的高光加强一下,比如胸部的光点,衣服上的金属边的高光点等。于是就得到了这样的效果:
在这里插入图片描述

再来看Matcap部分,直接用B通道控制范围,为了看得更清楚一些,所以暂时把高光去掉:

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

	//色阶化
	half halfLambert = GetHalfLambertDiffuse(i.worldPos, i.worldNormal);
	half toonVal = smoothstep(_GradationMin, _GradationMax, halfLambert);


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



	half3 finalRGB = col.rgb*toonVal + sssCol * (1 - toonVal);
	finalRGB = finalRGB * (1-ilmCol.b) +MatCapCol.rgb*ilmCol.b;

	half alpha = col.a;
    return half4(finalRGB,alpha);
}

在这里插入图片描述

这时候可以看到,之前十八铜人一般的全身反射已经没有了,变成了只有指定的部位出现了反射效果。
最后,把ILM对高光和反射的影响一起加上,就得到了:

在这里插入图片描述

四、完整Shader

Shader "azhao/ToonBodyLight"
{
    Properties
    {
        _BaseMap ("BaseMap", 2D) = "white" {}
		_SSSMap("SSSMap", 2D) = "white" {}
		_ILMMap("ILMMap", 2D) = "white" {}

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


		_GradationMin("GradationMin",Range(0.0,1.0)) = 0.0
		_GradationMax("GradationMax",Range(0.0,1.0)) = 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

        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;
			sampler2D _SSSMap;
			sampler2D _ILMMap;

			float4 _specColor;
			float _shininess;

			float _SpecAdd;
			float _GradationMin;
			float _GradationMax;
			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);
				half4 sssCol = tex2D(_SSSMap, i.uv);
				half4 ilmCol = tex2D(_ILMMap, i.uv);

				//色阶化
				half halfLambert = GetHalfLambertDiffuse(i.worldPos, i.worldNormal);
				half toonVal = smoothstep(_GradationMin, _GradationMax, halfLambert);

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


				half3 finalRGB = col.rgb*toonVal + sssCol  * (1 - toonVal)+_specColor* specVal*ilmCol.r+ _specColor * specVal*ilmCol.b*_SpecAdd;
				finalRGB = finalRGB * (1-ilmCol.b) +MatCapCol.rgb*ilmCol.b;

				half alpha = col.a;
                return half4(finalRGB,alpha);
            }
            ENDCG
        }

    }
}

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

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

相关文章

【嵌入式系统】思考题复习

嵌入式系统思考题 0. 名词解释1. 嵌入式系统概述2. ARM处理器体系结构3. ARM指令集4. S5PV210微处理器与接口5. ARM-Linux内核6. 嵌入式Linux文件系统7. 嵌入式Linux系统移植及调试8. 设备驱动程序设计9. QT图形界面应用程序开发基础10. SQLite数据库11. 嵌入式系统的开发设计案…

Qt6.5.1LTS搭建Android开发环境填坑

Qt6第二个LTS出来了,周日找时间安装并测试了Qt6.5LTS,安装我是按我之前的一个博客记录来做的,用的是国内境像,顺利快速安装完成,下面是设置的过程关键总结。 一、Devices Android设备(Device)选择 二、Kits &#xf…

网络安全-XSS的原理、攻击及防御

简介 跨站脚本攻击(全称Cross Site Scripting,为和CSS(层叠样式表)区分,简称为XSS)是指恶意攻击者在Web页面中插入恶意javascript代码(也可能包含html代码),当用户浏览网页之时,嵌入其中Web里面…

【C++开发】Qt+Tesseract实现文字识别的各种坑(已解决)

文章目录 Tesseract库下载Qt版本选择安装步骤 VS2017安装MSVC调试器安装构建套件的导入文字识别功能的检测 最近在给之前Qt医疗管理系统项目添加一个文字识别功能,但是在其中遇到非常多坑,花费了我比较多的时间(查阅了很多文章)&a…

基础学习——关于list、numpy、torch在float和int等数据类型转换方面的总结

系列文章目录 Numpy学习——创建数组及常规操作(数组创建、切片、维度变换、索引、筛选、判断、广播) Tensor学习——创建张量及常规操作(创建、切片、索引、转换、维度变换、拼接) 基础学习——numpy与tensor张量的转换 基础学习…

【软件测试】一个完整的项目流程是什么样的?

软件测试是使用人工或者自动的手段来运行或者测定某个软件系统的过程,其目的在于检验它是否满足规定的需求或弄清预期结果与实际结果之间的差别。 在软件投入使用前,要经过一系列的严格测试,才能保证交付质量。 一、引言 1.编写目的 本文档…

2023年度第四届全国大学生算法设计与编程挑战赛(春季赛)

目录 2023年度第四届全国大学生算法设计与编程挑战赛(春季赛)1、A2、Bx3、Cut4、Diff5、EchoN6、Farmer7、GcdGame8、HouseSub9、IMissYou!10、Jargonless 2023年度第四届全国大学生算法设计与编程挑战赛(春季赛) 1、A 题目描述…

【C++初阶】C/C++内存管理(没有对象的都进来看看吧~)

👦个人主页:Weraphael ✍🏻作者简介:目前学习C和算法 ✈️专栏:C航路 🐋 希望大家多多支持,咱一起进步!😁 如果文章对你有帮助的话 欢迎 评论💬 点赞&#x1…

2023 华为 Datacom-HCIE 真题题库 08/12--含解析

单项选择 1.[试题编号:190385] (单选题)以下关于BGP/MPLSIPVPN路由交互的描述,错误的是哪一项? A、PE与CE之间交互的是IPv4路由信息 B、出口PE可以通过BGP、IGP或静态路由的方式向远端CE发送IPv4路由 C、入口PE将从CE接收到的I…

ODOO随笔(二)—— Odoo16的主题样式变更

1 登陆界面的修改 系统默认的登陆界面,有更改odoo logo和去除“管理数据库”、“由Odoo提供支持”两个需求。 (1)更改odoo logo 系统管理员登陆后,选择菜单栏:设置——公司——管理公司 点击相机图标,上传…

Round#13 web专项部分wp

flask?jwt? 忘记密码处有secretkey 然后就是伪造session了,这里猜一下要什么 最后应该是_is_admin有用,我没细看,当时平台卡麻了 指正:改_user_id为1 ez_factors 注意到可以拼接命令,但是执行的回显只有数字 那么可以考虑读取后用od命令来转8进制读取 flask?jwt?(hard) …

2023.5.30 深信服 c++ 一面

深信服,c一面小记 导语面试内容重点问题解析弱引用弱在哪里?手撕memcpy水壶倒水问题 导语 最近开始面试,记录一下面试经历。   应该是会给我发感谢信的吧~我也是真的菜。工作的原因其实也没时间准备,最近工作还是挺忙的。另外一…

【云原生docker】

容器化越来越受欢迎,因为容器是: ●灵活:即使是最复杂的应用也可以集装箱化。 ●轻量级:容器利用并共享主机内核。 ●可互换:可以即时部署更新和升级。 ●便携式:可以在本地构建,部署到云&#…

LVGL学习(2):图片的转换和显示

我们在设计UI的过程中可能需要显示一些图片,本篇文章将介绍如何转换并显示一个固定的图片到lv_img中。 文章目录 1 图片转换1.1 GUI Guider1.2 在线转换 2 图片的显示 1 图片转换 和之前我写的一篇字体转换的文章一样:LVGL学习(1):中文字体…

基于牛顿拉夫逊的配电网潮流计算研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

C++语法(23)-- 模拟实现unordered_set和unordered_map

C语法(22)---- 哈希表的闭散列和开散列_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/130436178?spm1001.2014.3001.5501 1.重写HashTable 由于此时我们的实现与map跟set差不多,所以需要进行调整 1.重写节点…

html5前端学习

HTML5基本骨架 html标签 定义HTML文档&#xff0c;浏览器看到后就明白这个是HTML文档&#xff0c;所以其他元素要包裹在它里面&#xff0c;标签限定了文档的开始点和结束点。 <!DOCTYPE html> <html> </html> head标签 head标签用于定义文档的头部&…

实验篇(7.2) 05. 通过浏览器访问远端内网服务器 (SSL) ❀ 远程访问

【简介】直接将内网服务器映射成公网IP&#xff0c;可以方便的从任何地方访问服务器的指定端口&#xff0c;但是这种方式下&#xff0c;服务器是公开且暴露的。那有没有即方便、又比较安全的远程访问服务器的方法呢&#xff1f;我们来看看SSL VPN的Web模式。 SSL VPN介绍 从概念…

波司登云原生微服务治理探索

作者&#xff1a;曾孟琪&#xff08;山猎&#xff09; 背景 波司登创始于1976年&#xff0c;专注于羽绒服的研发、设计、制作&#xff0c;是全球知名的羽绒服生产商。波司登用一系列世人瞩目的辉煌成绩证明了自己的实力&#xff1a;连续26年全国销量领先&#xff0c;连续22年…

4种事务隔离级别 3种异常现象 死锁

4种事务隔离级别 & 3种异常现象 4种事务隔离级别和3种异常现象 事务隔离级别是指多个并发事务之间相互隔离的程度&#xff0c;用于控制事务对数据库的读取和写入操作的可见性和影响范围。在关系数据库管理系统&#xff08;RDBMS&#xff09;中&#xff0c;常见的事务隔离…