【Unity3D】素描特效
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 的计算如下:
fixed w0 = 1; // 白色纹理权值
fixed 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;
half2 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)素描特效

声明:本文转自【Unity3D】素描特效。
【Unity3D】素描特效的更多相关文章
- 极客标签互动课程系列 - Javascript生成SVG动画素描特效
课程描写叙述:在这个课程中,我们将介绍SVG.而且介绍怎样使用javascript来控制SVG生成素描动画效果 课程地址:http://www.gbtags.com/gb/gbliblist/21.h ...
- Unity3d粒子特效:制作火焰效果
效果 分析 真实的火焰效果,通常包括:火.火光.火星等组成部分,而火焰对周围环境的烘焙,可以通过灯光实现,如点光源. 针对火焰组成部分,我们可以创建对应的粒子系统组件,实现相应的效果,如下图所示: 1 ...
- 【Unity3D】基于粒子系统实现烟花特效
1 需求实现 粒子系统ParticleSystem 中介绍了粒子初始化.粒子发射.发射器形状.渲染器.碰撞.子发射器.拖尾等粒子系统的基本用法,本节将基于粒子系统实现烟花特效. 实现需求如下( ...
- PS 滤镜——素描算法(二)
利用另外一种算法完成素描特效的生成. %%% Sketch clc; clear all; Image=imread('4.jpg'); Image=double(Image); [row,col,l ...
- Python: PS 滤镜--素描
本文用 Python 实现 PS 滤镜中的素描特效,具体的算法原理和效果可以参考之前的博客: http://blog.csdn.net/matrix_space/article/details/386 ...
- Python图像处理丨5种图像处理特效
摘要:本篇文章主要讲解了图像常见的特效处理,从处理效果图.算法原理.代码实现三个步骤进行详细讲解,涉及图像素描特效.怀旧特效.光照特效.流年特效.图像滤镜等. 本文分享自华为云社区<[Pytho ...
- Unity塔防游戏开发
Unity3D塔防开发流程 配置环境及场景搭建编程语言:C#,略懂些许设计模式,如果不了解设计模式,BUG More开发工具:Unity3D编辑器.Visual Studio编译器开发建议:了解Uni ...
- Unity3D特效-场景淡入淡出
最近公司开始搞Unity3D..整个游戏..特效需求还是比较多的.关于UI部分的特效淡入淡出.看网上用的方法都是用个黑东东遮挡然后设置alpha这么搞....本大神感觉非常的low.而且很渣.故奋笔疾 ...
- unity3d 游戏插件 溶解特效插件 - Dissolve Shader
unity3d 游戏插件 溶解特效插件 - Dissolve Shader 链接: https://pan.baidu.com/s/1hr7w39U 密码: 3ed2
- Unity3D之挥动武器产生的剑痕特效
网维教程网 观看很多其它教程 眼下已知3种方法能够做这样的剑痕特效 1.尾随特效 2.程序实现动态面来处理剑痕动画. 3.美术实现剑痕动画,直接坐在模型动画里面 (由于我不会美术所以这个忽略 嘿嘿) ...
随机推荐
- vue【解决方案】页面/路由跳转后,滚动条消失,页面无法滚动
原因解析: vue项目中,页面/路由跳转后,body 的内联样式变成 overflow:hidden 解决方案: 使用路由守卫,在页面/路由跳转后,将body 的overflow设置为auto src ...
- golang技术栈常见网址
go所有,包含goadmin golang标准库文档 golang修养之路 Golang Profiling: 关于 pprof go问题 go语言设计与实现 go.mod解析 proto3 prot ...
- Windows服务程序管理器 - 开源研究系列文章
这些天弄了一个Windows服务程序管理器,主要是对需要的Windows服务程序进行管理.这个也能够将自己开发的服务程序注册到操作系统里去运行. 1. 项目目录: 目录见下图,对代码进行 ...
- PHP编程与系统开发
PHP开发环境配置 一.开发环境 1.XAMPP或LNMP.WNMP,先安装widows版本的XMAPP-5.6版本(PHP 5.6) 2.VSCode:微软开发的集成开发环境(IDE) 二.安装教程 ...
- 一篇文章告诉你什么是Java内存模型
在上篇 并发编程Bug起源:可见性.有序性和原子性问题,介绍了操作系统为了提示运行速度,做了各种优化,同时也带来数据的并发问题, 定义 在单线程系统中,代码按照顺序从上往下顺序执行,执行不会出现问题. ...
- obloader 基于典型场景数据导入
作者:刘书盛 热衷技术分享.编写技术文档 原创作品 oceanbase 数据库 原创内容未经授权不得随意使用,转载请联系小编并注明来源,谢谢! 预处理函数: 函数签名 返回类型 描述 LOWER(ch ...
- Linux 上的 .NET 崩溃了怎么抓 Dump
一:背景 1. 讲故事 训练营中有朋友问在 Linux 上如何抓 crash dump,在我的系列文章中演示的大多是在 Windows 平台上,这也没办法要跟着市场走,谁让 .NET 的主战场在工控 ...
- CANoe学习笔记(二):创建第一个事件触发帧(基于LIN)
内容: 创建一个事件触发帧: 包含几个不同无条件帧: 事件触发帧的触发: 事先准备: 创建三个文件夹,用来放不同类型文件: 工程创建 新建一个Lin工程,双击即可,然后命名为LINconf保存. 创建 ...
- 使用 Transformers 为多语种语音识别任务微调 Whisper 模型
本文提供了一个使用 Hugging Face Transformers 在任意多语种语音识别 (ASR) 数据集上微调 Whisper 的分步指南.同时,我们还深入解释了 Whisper 模型.Com ...
- DevOps|从腾讯TEG CDC解散聊技术中台价值和建设
近日一则腾讯TEG CDC整个部门解散的消息在很多群里炸了锅,有的唱衰互联网行业,有的唉声叹气,还有的甩锅到 AGI 的发展.总体上来说,这个事情的确已经发生了,我想从组织架构和整体效能这两方面来分析 ...