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】素描特效的更多相关文章

  1. 极客标签互动课程系列 - Javascript生成SVG动画素描特效

    课程描写叙述:在这个课程中,我们将介绍SVG.而且介绍怎样使用javascript来控制SVG生成素描动画效果 课程地址:http://www.gbtags.com/gb/gbliblist/21.h ...

  2. Unity3d粒子特效:制作火焰效果

    效果 分析 真实的火焰效果,通常包括:火.火光.火星等组成部分,而火焰对周围环境的烘焙,可以通过灯光实现,如点光源. 针对火焰组成部分,我们可以创建对应的粒子系统组件,实现相应的效果,如下图所示: 1 ...

  3. 【Unity3D】基于粒子系统实现烟花特效

    1 需求实现 ​ 粒子系统ParticleSystem 中介绍了粒子初始化.粒子发射.发射器形状.渲染器.碰撞.子发射器.拖尾等粒子系统的基本用法,本节将基于粒子系统实现烟花特效. ​ 实现需求如下( ...

  4. PS 滤镜——素描算法(二)

    利用另外一种算法完成素描特效的生成. %%% Sketch clc; clear all; Image=imread('4.jpg'); Image=double(Image); [row,col,l ...

  5. Python: PS 滤镜--素描

    本文用 Python 实现 PS 滤镜中的素描特效,具体的算法原理和效果可以参考之前的博客: http://blog.csdn.net/matrix_space/article/details/386 ...

  6. Python图像处理丨5种图像处理特效

    摘要:本篇文章主要讲解了图像常见的特效处理,从处理效果图.算法原理.代码实现三个步骤进行详细讲解,涉及图像素描特效.怀旧特效.光照特效.流年特效.图像滤镜等. 本文分享自华为云社区<[Pytho ...

  7. Unity塔防游戏开发

    Unity3D塔防开发流程 配置环境及场景搭建编程语言:C#,略懂些许设计模式,如果不了解设计模式,BUG More开发工具:Unity3D编辑器.Visual Studio编译器开发建议:了解Uni ...

  8. Unity3D特效-场景淡入淡出

    最近公司开始搞Unity3D..整个游戏..特效需求还是比较多的.关于UI部分的特效淡入淡出.看网上用的方法都是用个黑东东遮挡然后设置alpha这么搞....本大神感觉非常的low.而且很渣.故奋笔疾 ...

  9. unity3d 游戏插件 溶解特效插件 - Dissolve Shader

    unity3d 游戏插件 溶解特效插件 - Dissolve Shader   链接: https://pan.baidu.com/s/1hr7w39U 密码: 3ed2

  10. Unity3D之挥动武器产生的剑痕特效

    网维教程网 观看很多其它教程 眼下已知3种方法能够做这样的剑痕特效 1.尾随特效 2.程序实现动态面来处理剑痕动画. 3.美术实现剑痕动画,直接坐在模型动画里面 (由于我不会美术所以这个忽略 嘿嘿) ...

随机推荐

  1. 2022-12-15:寻找用户推荐人。写一个查询语句,返回一个客户列表,列表中客户的推荐人的编号都 不是 2。 对于示例数据,结果为: +------+ | name | +------+ | Wil

    2022-12-15:寻找用户推荐人.写一个查询语句,返回一个客户列表,列表中客户的推荐人的编号都 不是 2. 对于示例数据,结果为: ±-----+ | name | ±-----+ | Will ...

  2. Jenkins - 页面汉化

    Jenkins - 页面汉化 前言 对于不懂英文的人来讲,尤其是第一次使用Jenkins环境,看全英文的Jenkins页面是十分困难的: Jenkins对于不懂英文的用户有做汉化的插件包,但是汉化的不 ...

  3. vue全家桶进阶之路25:Vue2的停维通知

    Vue 2 的技术支持会持续多久?从官方发文来看,Vue 2.7 是当前.同时也是最后一个 Vue 2.x 的次级版本更新.Vue 2.7 会以其发布日期,即 2022 年 7 月 1 日开始计算,提 ...

  4. 「P2」试下1个半月能不能水出个毕设

    0.目标 将上个 springboot 项目 + html 中的html用Vue来重写,也就是在原springboot项目中集成Vue 1.在界面上,将html改成vue的形式 1.1.原html & ...

  5. 简要介绍django框架

    Django是一个高级的Python Web框架,它鼓励快速开发和干净.实用的设计. Django遵循MVC(模型-视图-控制器)设计模式,使得开发者能够更轻松地组织代码和实现功能.以下是Django ...

  6. [C#]插件编程框架 MAF 开发总结

    1. 什么是MAF和MEF? MEF和MEF微软官方介绍:https://learn.microsoft.com/zh-cn/dotnet/framework/mef/ MEF是轻量化的插件框架,MA ...

  7. hvv蓝初面试常见漏洞问题(下)

    hvv蓝初面试常见漏洞问题(上) 6.ssrf 服务端伪造请求 原理 服务端提供了向其他服务器应用获取数据的功能,而没有对目标地址做任何过滤和限制.攻击者进而利用其对内部资源进行攻击.(通俗来说:就是 ...

  8. 使用 ChatGPT 的 7 个技巧 | Prompt Engineering 学习笔记

    概述 前段时间在 DeepLearning 学了一门大火的 Prompt 的课程,吴恩达本人授课,讲的通俗易懂,感觉受益匪浅,因此在这里总结分享一下我的学习笔记. 为什么要学习 Prompt ? 因为 ...

  9. 聊聊Flink必知必会(二)

    Checkpoint与Barrier Flink是一个有状态的流处理框架,因此需要对状态做持久化,Flink定期保存状态数据到存储空间上,故障发生后从之前的备份中恢复,这个过程被称为Checkpoin ...

  10. 抓包分析RST报文

    大家好,我是蓝胖子,今天我们来分析下网络连接中经常出现的RST信号,连接中出现RST信号意味着这条链接将会断开,来看下什么时候会触发RST信号,这在分析连接断开的原因时十分有帮助. 本文的讲解视频已经 ...