【Unity3D】素描特效

news2024/11/17 5:34:15

1 非真实渲染

        法线贴图和凹凸映射中讲述了普通光照的渲染原理,实现的效果比较贴近真实世界(照相写实主义,Photorealism),非真实渲染(Non-Photorealism Rendering,NPR)在照相写实主义的基础上添加了一些风格处理,如:卡通、水彩、素描等风格。

        本文完整资源见→Unity3D素描特效。

2 素描特效原理

        素描特效不直接渲染漫反射效果,而是通过线条的疏密程度表现漫反射效果,即:较亮处线条稀疏,较暗处线条密集。

        线条采样方法:先计算顶点对应的漫反射强度,假设为 diffuse,再根据 diffuse 所处的区间在多个素描纹理(如下)中采样。

        diffuse 计算如下,其中 normal 为顶点对应的单位法线向量,lightDir 为顶点指向光源的单位方向向量。

float diffuse = max(0, dot(normal, lightDir));

        为方便划分区间,我们将 diffuse 乘以 7(6 个素描纹理 + 空白纹理),记为 factor,即 factor = diffuse * 7,我们将 factor 均匀划分 7 个区间,假设 factor 值对应的空白纹理和 line1 ~ line6 纹理的权值分别为 w0 ~ w6,则 w0 ~ w6 的计算如下:

float w0 = 1; // 白色纹理权值
float w1 = 0, w2 = 0, w3 = 0, w4 = 0, w5 = 0, w6 = 0; // line_1~line_6纹理权值
if (factor > 6) { // 区间: (6, 7]
	return; // 白色, 直接跳出
} else if (factor > 5) { // 区间: (5, 6]
	w1 = factor - 5;
} else if (factor > 4) { // 区间: (4, 5]
	w1 = factor - 4;
	w2 = 1 - w1;
} else if (factor > 3) { // 区间: (3, 4]
	w2 = factor - 3;
	w3 = 1 - w2;
} else if (factor > 2) { // 区间: (2, 3]
	w3 = factor - 2;
	w4 = 1 - w3;
} else if (factor > 1) { // 区间: (1, 2]
	w4 = factor - 1;
	w5 = 1 - w4;
} else { // 区间: [0, 1]
	w5 = factor;
	w6 = 1 - w5;
}
w0 = 1 - w1 - w2 - w3 - w4 - w5 - w6;

        根据 w0 ~ w6 对白色纹理和 line1 ~ line6 纹理进行加权求和,得到顶点对应的素描颜色。

3 素描特效实现

        SketchEffect.cs

using UnityEngine;

[DisallowMultipleComponent] // 不允许在同一对象上挂载多个该组件
public class SketchEffect : MonoBehaviour {
    private Material sketchMat; // 素描材质

    private void Awake() {
        sketchMat = Resources.Load<Material>("Sketch/Materials/SketchMat");
    }

    private void OnEnable() {
        Renderer[] renderers = GetComponentsInChildren<Renderer>();
        foreach (var renderer in renderers) { // 将rneder里的所有材质球都替换为sketchMat材质
            Material[] materials = new Material[renderer.sharedMaterials.Length];
            for (int i = 0; i < materials.Length; i++) {
                materials[i] = sketchMat;
            }
            renderer.materials = materials;
        }
    }
}

        说明:SketchEffect 脚本组件挂在需要渲染素描特效的对象上。 

        SketchEffect.shader

Shader "MyShader/SketchEffect" {
	Properties {
		_Color ("Color", Color) = (1, 1, 1, 1) // 背景颜色
		_Tilling ("Tilling", Float) = 1 // 纹理缩放, 值越大线条越密集
		_Line1 ("Line 1", 2D) = "white" {} // 素描纹理1
		_Line2 ("Line 2", 2D) = "white" {} // 素描纹理2
		_Line3 ("Line 3", 2D) = "white" {} // 素描纹理3
		_Line4 ("Line 4", 2D) = "white" {} // 素描纹理4
		_Line5 ("Line 5", 2D) = "white" {} // 素描纹理5
		_Line6 ("Line 6", 2D) = "white" {} // 素描纹理6
	}
	
	SubShader {
		Tags { "RenderType"="Opaque" "Queue"="Geometry"}

		Pass {
			Tags { "LightMode"="ForwardBase" }
			
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag 

			#include "UnityCG.cginc"
			
			fixed4 _Color; // 背景颜色
			float _Tilling; // 纹理缩放, 值越大线条越密集
			sampler2D _Line1; // 素描纹理1
			sampler2D _Line2; // 素描纹理2
			sampler2D _Line3; // 素描纹理3
			sampler2D _Line4; // 素描纹理4
			sampler2D _Line5; // 素描纹理5
			sampler2D _Line6; // 素描纹理6

			struct v2f {
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				fixed4 w1 : TEXCOORD1; // 前三张素描纹理的权值(w维存储白色)
				fixed3 w2 : TEXCOORD2; // 后三张素描纹理的权值
			};

			void getLineWeights(float diffuse, out fixed4 w1, out fixed3 w2) { // 根据漫反射值获取6张素描纹理的权重
				float factor = diffuse * 7.0;
				w1 = fixed4(0, 0, 0, 1);
				w2 = fixed3(0, 0, 0);
				if (factor > 6) { // 区间: (6, 7]
					return; // 白色, 直接跳出
				} else if (factor > 5) { // 区间: (5, 6]
					w1.x = factor - 5;
				} else if (factor > 4) { // 区间: (4, 5]
					w1.x = factor - 4;
					w1.y = 1 - w1.x;
				} else if (factor > 3) { // 区间: (3, 4]
					w1.y = factor - 3;
					w1.z = 1 - w1.y;
				} else if (factor > 2) { // 区间: (2, 3]
					w1.z = factor - 2;
					w2.x = 1 - w1.z;
				} else if (factor > 1) { // 区间: (1, 2]
					w2.x = factor - 1;
					w2.y = 1 - w2.x;
				} else { // 区间: [0, 1]
					w2.y = factor;
					w2.z = 1 - w2.y;
				}
				w1.w = 1 - w1.x - w1.y - w1.z - w2.x - w2.y - w2.z; // w维存储白色
			}
			
			v2f vert(appdata_base v) {
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex); // 计算裁剪坐标系中顶点坐标, 等价于: mul(unity_MatrixMVP, v.vertex)
				o.uv = v.texcoord * _Tilling; // 对uv坐标进行缩放, 用于调整素描图像的稀疏和宽窄程度
				fixed3 worldLightDir = normalize(WorldSpaceLightDir(v.vertex)); // 计算世界空间中顶点指向光源的向量
				fixed3 worldNormal = UnityObjectToWorldNormal(v.normal); // 计算世界空间中顶点法线向量
				fixed diffuse = max(0, dot(worldLightDir, worldNormal)); // 计算漫反射值
				getLineWeights(diffuse, o.w1, o.w2); // 根据漫反射值获取6张素描纹理的权重
				return o; 
			}
			
			fixed4 frag(v2f i) : SV_Target {			
				fixed4 lineTex1 = tex2D(_Line1, i.uv) * i.w1.x;
				fixed4 lineTex2 = tex2D(_Line2, i.uv) * i.w1.y;
				fixed4 lineTex3 = tex2D(_Line3, i.uv) * i.w1.z;
				fixed4 lineTex4 = tex2D(_Line4, i.uv) * i.w2.x;
				fixed4 lineTex5 = tex2D(_Line5, i.uv) * i.w2.y;
				fixed4 lineTex6 = tex2D(_Line6, i.uv) * i.w2.z;
				fixed4 whiteColor = fixed4(1, 1, 1, 1) * i.w1.w;
				fixed4 sketchColor = lineTex1 + lineTex2 + lineTex3 + lineTex4 + lineTex5 + lineTex6 + whiteColor;
				return fixed4(sketchColor.rgb * _Color.rgb, 1.0);
			}
			
			ENDCG
		}
	}

	FallBack "Diffuse"
}

        说明:在 Assets/Resources/Sketch/Materials 目录下创建 Material,重命名为 SketchMat ,将 SketchEffect.shader 赋给 SketchMat 材质,并将 line1 ~ line6 纹理拖拽到 SketchMat 材质中对应位置,调整 Tilling 属性的值。

4 运行效果

        1)原图

        2)素描特效

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

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

相关文章

7-数组创建函数还有哪些?【视频版】

目录 问题视频解答 问题 视频解答 点击观看&#xff1a; 7-数组创建函数还有哪些&#xff1f;

基于IDEA操作springboot项目构建docker镜像部署到云服务器并在idea把镜像推送到harbor仓库

云服务器系统环境: 华为云 Ubuntu 9.3.0-17ubuntu1~20.04 1.ECS准备docker相关环境 1.1ECS安装docker 一行一行执行 都是从官网找的命令 sudo -i apt update apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common curl -fsSL htt…

Linux学习之screen和系统日志

screen yum install -y screen安装screen。 输入screen就可以进入screen环境。 在screen环境中执行tail -f /var/log/messages。 可以先按Ctrla&#xff0c;这时没有反应&#xff0c;需要紧接着迅速按d才能退出screen环境。 使用screen -r 11326还可以进入到刚刚挂起的s…

【Rust】基本语法

文章目录 变量与可变性变量与常量Shadowing&#xff08;隐藏&#xff09;数据类型标量类型1.整数类型2.浮点类型3.布尔类型4.字符类型 复合类型1.Tuple2.数组 函数if表达式循环1.loop2.while3.for 变量与可变性 声明变量使用let关键字&#xff0c;默认情况下&#xff0c;变量是…

Android 开发环境搭建

系列文章 MySQL安装教程&#xff08;详细&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/126037520 MySQL卸载教程&#xff08;详细&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/129279265 …

【C++】类型转换和IO流

C完结 文章目录 前言一、C的四种类型转换二、IO流总结 前言 首先我们看看C语言中的类型转换&#xff1a; 在 C 语言中&#xff0c;如果 赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&#xff0c;或者返回值类型与 接收返回值类型不一致时&#xff0c;就需…

tmux-解决ssh长时间不操作便会话结束的问题

Tmux 是一个终端复用器&#xff08;terminal multiplexer&#xff09;&#xff0c;非常有用&#xff0c;属于常用的开发工具。 如果电脑死机会话中断的话会导致服务器上基于bash的所有进程都被强制关闭。tmux确实是解决这个问题的好办法&#xff0c;而且很容易上手&#xff0c…

帝国CMS自动刷新首页和自定义列表页:实现网站内容的自动更新

本文将介绍一种实现帝国CMS自动刷新首页和自定义列表页的方法。 以下是具体的实现步骤&#xff1a; 1、设置自定义key 添加网站 设置发布网站 发布成功 首先&#xff0c;在您的帝国CMS根目录下创建一个名为"htmlindex"的文件夹&#xff08;您可以自行命名&#xff…

基于网格式搜索SVM方法的电力负荷预测

来源公众号:320科技工作室 随着生活质量的提高&#xff0c;人们对电的需求不断上升&#xff0c;电力系统的发展变得尤为重要。准确的电力负荷预测能够使电力部门降低发电成本&#xff0c;合理安排设备维护以及节省能源&#xff0c;并为电力规划以及制定合理发电量提供相关依据…

PACS医学影像系统(完整版)

一、PACS影像存取与传输系统以实现医学影像数字化存储、诊断为核心任务&#xff0c;从医学影像设备&#xff08;如CT、CR、DR、MR、DSA、RF等&#xff09;获取影像&#xff0c;集中存储、综合管理医学影像及病人相关信息&#xff0c;建立数字化工作流程。 二、系统可实现检查预…

【Echarts系列】— echarts饼图、圆环图配置代码图文详解

前言 简介&#xff1a;本文将从头开始&#xff0c;带你快速上手 echarts最常用图例—饼图 准备&#xff1a;请自行先将echarts图例引入你的项目&#xff0c;本文不多介绍。&#xff08;引入 echarts教程&#xff1a;http://t.csdn.cn/mkTa4&#xff09; 心得&#xff1a;echar…

LangChain 基于 ChatGPT 整合 Google 搜索 强化问答应用

一、LangChain Agents LangChain 中的 Agents 是什么呢&#xff0c;对于官方的描述是&#xff1a;某些应用程序不仅需要预先确定的对 LLM/其他工具的调用链&#xff0c;还可能需要依赖于用户输入的未知链。在这些类型的链中&#xff0c;有一个 Agents 可以访问一套工具。根据用…

【ISO26262】汽车功能安全第4部分:系统层面

第4部分:产品开发:系统层面 GB/T34590的本部分规定了车辆在系统层面产品开发的要求,包括: ———启动系统层面产品开发; ———技术安全要求的定义; ———技术安全概念; ———系统设计; ———相关项集成和测试; ———安全确认; ———功能安全评估;及 ———生产发布。

程序运行期间加载动态库

note 相关系统调用接口&#xff1a;dlopen\dlclose\dlerror\dlsym code math.c int add(int x, int y) {return (xy); }int sub(int x, int y) {return (x-y); } main.c #include <stdio.h> #include <stdlib.h> #include <dlfcn.h>int main(int argc, …

VMware网络模式大揭秘:打造高效稳定的虚拟机通讯网络

文章目录 一 VMWare网络连接1.1 查看VM网络模式1.2 VMWare三种网络模式1.2.1 桥接模式&#xff08;Bridged Mode&#xff09;1.2.2 主机模式&#xff08;Host-Only Mode&#xff09;1.2.3 NAT模式&#xff08;Network Address Translation Mode&#xff09; 1.3 补充知识&#…

linux基础命令总结

目录标题 目录操作1.绝对路径和相对路径2.目录的基本操作3.chomd、chown、chgrp4.cd命令 文件操作1.touch2.more4.cat5.grep6.rm7.cp8.mv9.find 磁盘操作1.df2.du date、passwd1.date2.passwd su、clear、man、who1.su2.man3.clear4.who unameuptimefreepsps与grep、kill1.与gr…

【Leetcode】动态规划 刷题训练(八)

文章目录 413. 等差数列划分状态转移方程完整代码 978. 最长湍流子数组题目解析状态转移方程f[i]状态转移方程g[i]状态转移方程 完整代码 139. 单词拆分状态转移方程初始化完整代码 413. 等差数列划分 点击查看&#xff1a;等差数列划分 如果一个数列 至少有三个元素 &#x…

【操作系统】键盘敲入字母时,操作系统期间发生了什么?

【操作系统】键盘敲入字母时&#xff0c;操作系统期间发生了什么&#xff1f; 参考资料&#xff1a; 键盘敲入 A 字母时&#xff0c;操作系统期间发生了什么&#xff1f; 【操作系统】浅谈 Linux 中的中断机制 文章目录 【操作系统】键盘敲入字母时&#xff0c;操作系统期间发…

小驰私房菜_26_YUV数据存在数据对齐,工具打开花图时如何处理?

【问题背景】 在Qcom Camx框架下&#xff0c;dump的yuv,yuv数据有时会存在数据对齐&#xff0c;也就是app端下发的size和我们dump出来的size是不一致的。 这个时候&#xff0c;我们用yuv工具查看yuv数据的时候&#xff0c;宽高如果直接设置的app端下发的size&#xff0c;这个时…

案例解析 | 虚拟数智人“岭梅香”——民间博物馆文化探寻者

TA 是湾区民间文化探寻者 还是广东民间博物馆宣传大使 万里归来颜愈少 笑时犹带“岭梅香” 虚拟数智人 是文博行业走进“元宇宙”的“探路者” 为了践行国家文化数字化战略&#xff0c;按照文化和旅游部“上云用数赋智”的要求&#xff0c;南方都市报、N视频联合广州虚拟…