Unity制作二次元卡通渲染角色材质——2、色阶化光影的多种做法对比

news2024/11/28 12:50:45

Unity制作二次元材质角色


回到目录

大家好,我是阿赵。
这里继续讲二次元角色渲染。之前说过,最基本的卡通渲染,包含了色阶化光影和描边二个元素。所以这里先来说一下色阶化光影的多种做法对比。

一、光照模型和色阶化的说明

从上一篇文章里面可以知道,这个模型提供了2套贴图,分别是baseMap和sssMap
在这里插入图片描述

baseMap
在这里插入图片描述

sssMap

这两张贴图的作用,很明显作为亮部和暗部显示用的。
那么需要怎样使用这两张贴图呢?
其实很简单,还是自定义光照模型。我之前写过文章介绍怎样自己写光照模型。然后我之后做了很多例子,如果各位如果有留意看,估计也发现,自定义光照模型是很多效果的基础。
这里我也同样的先做一个HalfLambert漫反射光照:

//获取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;
}
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);
half3 finalRGB = col.rgb*halfLambert + sssCol * (1 - halfLambert);
half alpha = col.a;
return half4(finalRGB,alpha);
}

在这里插入图片描述
这个时候,通过获取漫反射的光照值来混合baseMap和sssMap,会得到这种效果。
不过这只是正常普通的HalfLambert光照效果,在卡通渲染里面,因为是模拟漫画的涂色方式,光影之间一般来说会有一个硬边的过渡,比如:
在这里插入图片描述

这种情况,我们会称为色阶化。意思就是,把一个渐变过渡的光影效果,变成了一段一段色阶的跳跃性过渡。

二、实现色阶化的多种手段介绍

做色阶化的方法比较多,比如:

1、通过一个色阶图去采样

这个方法需要准备一张色阶图:
在这里插入图片描述

然后通过光照模型的值作为x坐标去采样这张贴图

half4 frag (v2f i) : SV_Target
{
//色阶化
half halfLambert = GetHalfLambertDiffuse(i.worldPos, i.worldNormal);
half4 GradationCol = tex2D(_GradationMap, float2(halfLambert, 0.5));
return GradationCol;
}

会得到这个效果:
在这里插入图片描述

可以看出,刚才的色阶图的三个颜色都出现在模型身上了,而且三段颜色的占比,可以通过调节色阶图的颜色范围来控制。
接下来可以把这个色阶图的采样结果,和baseMap做一个混合,就得到了:

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);
half4 GradationCol = tex2D(_GradationMap, float2(halfLambert, 0.5));

half3 finalRGB = col.rgb*GradationCol+ sssCol*(1- GradationCol);
half alpha = col.a;
return half4(finalRGB,alpha);
}

在这里插入图片描述

先忽略脸部,之后我会有专门的一篇文章讲脸部的问题。看一下身体的部分,可以看出现在的影子已经变成了色阶化了。
色阶图的做法的好处是,喜欢多少段色阶影子都可以,可以通过调整色阶图来控制影子的深浅和范围。

2、使用渐变图做色阶

色阶图其实也有缺点,如果想做到某些部位的色阶化强烈一点,某些部位的色阶化弱一点,其实是很难做到的。
所以出现了另外一种色阶图的做法,是渐变的
在这里插入图片描述

可以看出,这张渐变图下面是硬过渡,上面是软过渡,如果我们采样的时候,可以不同部位控制采样uv的y轴不一样,那么得到的软硬过渡程度也可以不一样。代码的写法和上面的色阶图是一样的,只是采样uv的y坐标不一样,所以就不重复写一遍了,只说一下原理。
那么怎样去控制UV坐标的y轴呢?方法很多,给一张专门的贴图去控制,或者直接用顶点颜色的某个通道去控制,都可以。
一般会使用顶点颜色去控制,配合着实时编辑顶点颜色的工具,可以比较快速的看到效果的改变。

3、使用step函数

不使用其他贴图,而是用step函数,也能实现色阶化:

half4 frag (v2f i) : SV_Target
{
//色阶化
half halfLambert = GetHalfLambertDiffuse(i.worldPos, i.worldNormal);
half toonVal = step(0, halfLambert- _stepVal);
return half4(toonVal.xxx,1);
}

在这里插入图片描述

加上了baseMap和sssMap的混合,效果就是:
在这里插入图片描述

step的做法,好处是简单,不需要额外的贴图资源,直接用一个变量就能控制阴影的范围。

4、使用smoothstep函数

step函数的做法是很简单的,但有一个缺点,不能控制阴影边缘的软硬程度
所以可以使用smoothstep函数来替代step函数,这样就可以控制阴影的软硬程度

half4 frag (v2f i) : SV_Target
{
//色阶化
half halfLambert = GetHalfLambertDiffuse(i.worldPos, i.worldNormal);
half toonVal = smoothstep(_GradationMin, _GradationMax, halfLambert);
return half4(toonVal.xxx,1);
}

在这里插入图片描述

可以看到,在使用smoothstep函数之后,通过控制smoothstep的最大值和最小值的参数,我们实现了不同部位的影子过渡程度不一样的效果了,甚至可以在某些部位减弱影子的效果。
混合了baseMap和sssMap,就是这种效果:
在这里插入图片描述

做色阶化的方法很多,可以自己再发散一下思维。已经列举出的几种方式,综合评价了一下,我觉得用smoothstep函数的方法会比较容易实现,容易控制,然后效果也还不错。所以我在这个例子里面最终是使用了smoothstep函数的方法来做色阶化光影的。

三、完整Shader

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

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

			//获取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;
			}


            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);

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


				half3 finalRGB = col.rgb*toonVal + sssCol * (1 - toonVal);
				half alpha = col.a;
                return half4(finalRGB,alpha);
            }
            ENDCG
        }

    }
}

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

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

相关文章

拿捏-哈夫曼树构建及编码生成(建议收藏)

文章目录 哈夫曼树的基本概念哈夫曼树的构建构建思路代码实现 哈夫曼编码的生成编码生成思路代码实现 完整代码展示及代码测试 哈夫曼树的基本概念 在认识哈夫曼树之前,你必须知道以下几个基本术语: 1、什么是路径? 在一棵树中,从…

Kafka 分区

分区是 Kafka 的核心功能,对于 Kafka 的存储结构、消息的生产消费方式都至关重要。 Partition(分区) 每 topic 都可以分成多分区,每个分区都是一组有序的、不可变的记录序列,每个分区在存储层面是 append log 文件。…

python中Requests发送json格式的post请求方法

问题:做requests请求时遇到如下报错: {“code”:“500”,“message”:"JSON parse error: Cannot construct instance of com.bang.erpapplication.domain.User (although at least one Creator exists): no String-argument constructor/factory …

唠一唠程序员的那些事

作为一名互联网摸爬滚打多年的老兵,我可以从以下角度提供一些信息: 加班是家常便饭:程序员往往需要加班来满足项目需求或完成任务。这意味着他们经常会在晚上、周末或节假日工作。代码不仅仅是工作:对于大多数程序员来说&#xff…

【C++】成员对象和成员函数分开存储

欢迎来到博主 Apeiron 的博客,祝您旅程愉快 ! 时止则止,时行则行。动静不失其时,其道光明。 目录 1、缘起 2、详解 3、代码清单 1 3.1、类中定义成员变量 3.2、类中定义成员函数 4、代码清单 2 5、总结 1、缘起 “成员变量…

SpringBoot 增强Controller方法,@ControllerAdvice注解的使用

参考资料 ControllerAdvice 用法SpringBoot使用ControllerAdvice注解 目录 一. ControllerAdvice注解作用二. ControllerAdvice注解 assignableTypes属性2.1 ControllerAdvice增强类2.2 Controller层2.3 效果 三. ControllerAdvice注解 basePackages属性3.1 ControllerAdvic…

踩坑|以为是Redis缓存没想到却是Spring事务!

前言 最近碰到了一个Bug,折腾了我好几天。并且这个Bug不是必现的,出现的概率比较低。一开始我以为是旧数据的问题,就让测试重新生成了一下数据,重新测试。由于后面几轮测试均未出现,我也就没太在意。 可惜好景不长&…

【LeetCode】260. 只出现一次的数字 III

260. 只出现一次的数字 III(中等) 思路 这道题是136. 只出现一次的数字 的进阶版,需要找出两个仅出现一次的元素。有了上一题的基础,我们很容易就想到要用异或来解决,但是由于这题最终会剩下两个不同的元素&#xff0…

设置和使用DragGAN:搭建非官方的演示版

DragGAN的官方版还没有发布,但是已经有非官方版的实现了,我们看看如何使用。DragGAN不仅让GAN重新回到竞争轨道上,而且为GAN图像处理开辟了新的可能性。正式版本将于本月发布。但是现在已经可以在一个非官方的演示中试用这个新工具了 DragGAN…

数据结构:二叉树(初阶)

朋友们、伙计们,我们又见面了,本期来给大家解读一下二叉树方面的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入门到精通 …

Unix/C/C++进阶--SocketCAN 编程

Unix/C/C进阶--SocketCAN 编程 1 介绍1.1 socketcan 简介1.2 can 发展历程1.3 can总线优点 2 知识点2.1 CAN详解--书籍、网站2.2 CAN详解--CAN与com口介绍2.3 CAN详解--各家CAN分析仪与软件的比较2.4 转载:CAN总线终端电阻2.5 如何破解汽车--CAN协议(can…

3.8 电路布线

博主简介:一个爱打游戏的计算机专业学生博主主页: 夏驰和徐策所属专栏:算法设计与分析 1.最优子结构的证明: 我的理解: 对于电路布线问题的最优子结构性质,我们可以通过数学推导进行证明。下面是对证明的…

conda在 powershell下不能激活虚拟环境

这里写自定义目录标题 问题原因解决办法增加环境变量修改PowerShell 策略初始化conda环境安装或更新conda 结果 问题原因 powershell正常是不行的,但是在cmd中是可以的 问题产生的原因有很多: 必须无法识别activate.bat激活无反应 解决办法 增加环…

【JavaSE】Java基础语法(四十六):枚举

文章目录 1. 概述2. 定义格式3. 枚举的特点4. 枚举的方法 1. 概述 枚举是一种特殊的数据类型,它列出了一组预定义的常量,并使用标识符来引用这些常量。枚举的用途很广泛,下面列举了几个常见的应用场景: 管理常量:如果您…

计算机组成原理---第三章存储系统 习题详解版

(一)精选课内习题 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 (二)精选课后习题 1.设有一个具有20位地址和32位字长的存储器,问: (1)该存储器能存储多少个字节的信息? (2)如果存储器由512k8位的SR…

Linux :: vim 编辑器:详解:文本复制/粘贴/剪切/删除 与 撤销普通操作及撤销撤销操作

前言:本篇是 Linux 基本操作篇章的内容! 笔者使用的环境是基于腾讯云服务器:CentOS 7.6 64bit。 学习集: C 入门到入土!!!学习合集Linux 从命令到网络再到内核!学习合集 前文&#x…

chatgpt赋能python:Python去掉None:提高代码效率,优化SEO

Python去掉None:提高代码效率,优化SEO 作为一名有10年Python编程经验的工程师,我发现Python中会频繁出现None类型的变量。这种情况在代码中一旦过多,就会影响程序的效率,同样也会影响SEO的排名。因此,为提…

【数据仓库架构】什么是 Azure Synapse,它与 Azure Data Bricks 有何不同?

Azure Synapse Analytics 是一项针对大型公司的无限信息分析服务,它被呈现为 Azure SQL 数据仓库 (SQL DW) 的演变,将业务数据存储和宏或大数据分析结合在一起。 在处理、管理和提供数据以满足即时商业智能和数据预测需求时,Synapse 为所有工…

Hive学习---5、文件格式和压缩、企业级调优

1、文件格式和压缩 1.1 Hadoop压缩概述 由于Hive是相当于与Hadoop的客户端,所以hadoop会啥压缩,Hive基本就会啥压缩。 压缩格式算法文件扩展名是否可切分DEFLATEDEFLATE.deflate否GzipDEFLATE.gz否bzip2bzip2.bz2是LZOLZO.lzo是SnappySnappy.snappy否…

word恢复和粘贴按钮变灰色,不可用怎么办?

如果 Word 中的恢复和粘贴按钮变成灰色,可能是由于以下原因之一: 1. 文档处于只读模式。 2. 与 Office 相关的某些组件已损坏或缺失。 3. Word 的文件权限被配置为只读。 以下是一些可能的解决方法: 1. 检查文档是否处于只读模式。 如果是…