百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

Shader知识普及之:素描效果的实现

csdh11 2024-12-12 11:18 4 浏览

本篇文章我们一起来学习下如何用shader为模型添加素描效果。

素描画(丢勒作品)


我们观察下素描画作,可以借鉴到素描风格模型的大致实现思路:事先我们要准备六张不同疏密程度、不同颜色深浅的素描贴图,根据模型表面情况,混合贴图比例到模型,实现模型素描化。


场景动物模型(素描效果处理)


我们先来看一下在Unity中对动物模型的shader代码处理过程:

Shader "Unlit/Sketch"

{

Properties

{

_Color("Color",Color) = (1,1,1,1)

_TileFactor("TileFactor", Range(0, 10)) = 1

_Hatch0("Hatch0",2D)="white"{}

_Hatch1("Hatch1",2D) = "white"{}

_Hatch2("Hatch2",2D) = "white"{}

_Hatch3("Hatch3",2D) = "white"{}

_Hatch4("Hatch4",2D) = "white"{}

_Hatch5("Hatch5",2D) = "white"{}

_OutlineFactor("OutlineFactor",Range(0.0,0.1))=0.01

}

SubShader

{

Tags{ "Queue" = "Transparent" }

Pass

{

Cull Front

ZWrite Off

Offset 1,1

CGPROGRAM

#include "UnityCG.cginc"

#pragma vertex vert

#pragma fragment frag

float _OutlineFactor;

struct v2f

{

float4 pos : SV_POSITION;

};

v2f vert(appdata_full v)

{

v2f o;

o.pos = UnityObjectToClipPos(v.vertex);

float3 vnormal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);

float2 offset = TransformViewToProjection(vnormal.xy);

o.pos.xy += offset * _OutlineFactor;

return o;

}

fixed4 frag(v2f i) : SV_Target

{

return float4(0,0,0,1);

}

ENDCG

}

Pass

{

CGPROGRAM

#include "UnityCG.cginc"

#include "Lighting.cginc"

#include "AutoLight.cginc"

#pragma vertex vert

#pragma fragment frag

#pragma multi_compile_fwdbase

float4 _Color;

float _TileFactor;

sampler2D _Hatch0;

sampler2D _Hatch1;

sampler2D _Hatch2;

sampler2D _Hatch3;

sampler2D _Hatch4;

sampler2D _Hatch5;

struct v2f

{

float2 uv : TEXCOORD0;

float4 vertex : SV_POSITION;

float3 hatchWeights0:TEXCOORD1;

float3 hatchWeights1:TEXCOORD2;

SHADOW_COORDS(4)

float3 worldPos:TEXCOORD3;

};

v2f vert (appdata_full v)

{

v2f o;

o.vertex = UnityObjectToClipPos(v.vertex);

o.uv = v.texcoord* _TileFactor;

float3 worldLightDir = normalize(WorldSpaceLightDir(v.vertex));

float3 worldNormal = UnityObjectToWorldNormal(v.normal);

float diffuse = max(0, dot(worldLightDir, worldNormal));

o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz ;

o.hatchWeights0 = float3(0, 0, 0);

o.hatchWeights1 = float3(0, 0, 0);

float hatchFactor = diffuse * 7.0;

if (hatchFactor > 6.0) {

}

else if (hatchFactor > 5.0) {

o.hatchWeights0.x = hatchFactor - 5.0;

}

else if (hatchFactor > 4.0) {

o.hatchWeights0.x = hatchFactor - 4.0;

o.hatchWeights0.y = 1.0 - o.hatchWeights0.x;

}

else if (hatchFactor > 3.0) {

o.hatchWeights0.y = hatchFactor - 3.0;

o.hatchWeights0.z = 1.0 - o.hatchWeights0.y;

}

else if (hatchFactor > 2.0) {

o.hatchWeights0.z = hatchFactor - 2.0;

o.hatchWeights1.x = 1.0 - o.hatchWeights0.z;

}

else if (hatchFactor > 1.0) {

o.hatchWeights1.x = hatchFactor - 1.0;

o.hatchWeights1.y = 1.0 - o.hatchWeights1.x;

}

else {

o.hatchWeights1.y = hatchFactor;

o.hatchWeights1.z = 1.0 - o.hatchWeights1.y;

}

TRANSFER_SHADOW(o);

return o;

}

fixed4 frag (v2f i) : SV_Target

{

float4 hatchTex0 = tex2D(_Hatch0, i.uv) * i.hatchWeights0.x;

float4 hatchTex1 = tex2D(_Hatch1, i.uv) * i.hatchWeights0.y;

float4 hatchTex2 = tex2D(_Hatch2, i.uv) * i.hatchWeights0.z;

float4 hatchTex3 = tex2D(_Hatch3, i.uv) * i.hatchWeights1.x;

float4 hatchTex4 = tex2D(_Hatch4, i.uv) * i.hatchWeights1.y;

float4 hatchTex5 = tex2D(_Hatch5, i.uv) * i.hatchWeights1.z;

float4 whiteColor = float4(1, 1, 1, 1)*(1 - i.hatchWeights0.x - i.hatchWeights0.y - i.hatchWeights0.z - i.hatchWeights1.x - i.hatchWeights1.y - i.hatchWeights1.z);

float4 hatchColor = hatchTex0 + hatchTex1 + hatchTex2 + hatchTex3 + hatchTex4 + hatchTex5+ whiteColor;

UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

return float4(hatchColor.rgb*_Color.rgb*atten, 1.0);

}

ENDCG

}

}

}


首先对模型进行描边操作,通过计算光源与物体表面漫反射值确定权重系数,从而确定贴图采集情况。


下面我们对逻辑进行拆分和解析:

描边逻辑:使用两个通道,其中一个通道沿法线方向挤出一点,输出形成描边的颜色

投影转换操作:将法线方向转换到最终投影阶段输出并进行偏移操作:

float3 vnormal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);

float2 offset = TransformViewToProjection(vnormal.xy);

o.pos.xy += offset * _OutlineFactor;

计算模型表面漫反射:

float diffuse = max(0, dot(worldLightDir, worldNormal));

o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz ;

对漫反射结果与不同贴图特征进行混合操作:漫反射越暗,线条越密集

float hatchFactor = diffuse * 7.0;

if (hatchFactor > 6.0) {

}

else if (hatchFactor > 5.0) {

o.hatchWeights0.x = hatchFactor - 5.0;

}

else if (hatchFactor > 4.0) {

o.hatchWeights0.x = hatchFactor - 4.0;

o.hatchWeights0.y = 1.0 - o.hatchWeights0.x;

}

else if (hatchFactor > 3.0) {

o.hatchWeights0.y = hatchFactor - 3.0;

o.hatchWeights0.z = 1.0 - o.hatchWeights0.y;

}

else if (hatchFactor > 2.0) {

o.hatchWeights0.z = hatchFactor - 2.0;

o.hatchWeights1.x = 1.0 - o.hatchWeights0.z;

}

else if (hatchFactor > 1.0) {

o.hatchWeights1.x = hatchFactor - 1.0;

o.hatchWeights1.y = 1.0 - o.hatchWeights1.x;

}

else {

o.hatchWeights1.y = hatchFactor;

o.hatchWeights1.z = 1.0 - o.hatchWeights1.y;

}

混合白色基础颜色信息:漫反射深色部分权重越大,白色越少

float4 whiteColor = float4(1, 1, 1, 1)*(1 - i.hatchWeights0.x - i.hatchWeights0.y - i.hatchWeights0.z - i.hatchWeights1.x - i.hatchWeights1.y - i.hatchWeights1.z);

将所以颜色信息进行混合操作

float4 hatchColor = hatchTex0 + hatchTex1 + hatchTex2 + hatchTex3 + hatchTex4 + hatchTex5+ whiteColor;

叠加阴影效果

UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

返回最终效果

return float4(hatchColor.rgb*_Color.rgb*atten, 1.0);

效果调整:

我们设置一个范围0-10的贴图平铺系数,修改系数处理贴图疏密程度

相关代码:o.uv = v.texcoord* _TileFactor;


调整平铺系数大小,实现线条稀疏密度的效果调整,如下图

设置高密度线条

设置低密度线条


准备好六张素描纹理贴图,在顶点着色阶段计算逐顶点的漫反射光照,通过光照结果决定六张纹理混合权重,然后将数据传输到片元着色器,片元着色器再根据权重结果混合六张纹理的采样结果。


本期的素描风格shader到此就结束了,我们下期再见!

相关推荐

Micheal Nielsen's神经网络学习之二

依然是跟着MichaelNielsen的神经网络学习,基于前一篇的学习,已经大概明白了神经网络的基本结构和BP算法,也能通过神经网络训练数字识别功能,之后我试验了一下使用神经网络训练之前的文本分类,...

CocoaPods + XCTest进行单元测试 c单元测试工具

在使用XCTest进行单元测试时,我们经常会遇到一些CocoaPods中的开源框架的调用,比如“Realm”或“Alamofire”在测试的时候,如果配置不当,会导致“frameworknotfo...

Java基础知识回顾第四篇 java基础讲解

1、&和&&的区别作为逻辑运算符:&(不管左边是什么,右边都参与运算),&&(如果左边为false,右边则不参与运算,短路)另外&可作为位运算符...

项目中的流程及类似业务的设计模式总结

说到业务流程,可能是我做过的项目中涉及业务最多的一个方面了。除了在流程设计之外,在一些考核系统、产业审批、还有很多地方,都用到相似的设计思路,在此一并总结一下。再说到模式,并不是因为流行才用这个词,而...

联想三款显示器首批获得 Eyesafe Certified 2.0 认证

IT之家7月31日消息,据外媒报道,三款全新联想显示器是全球首批满足EyesafeCertified2.0的设备。据报道,联想获得EyesafeCertified2.0认证的显...

maven的生命周期,插件介绍(二) 一个典型的maven构建生命周期

1.maven生命周期一个完整的项目构建过程通常包括清理、编译、测试、打包、集成测试、验证、部署等步骤,Maven从中抽取了一套完善的、易扩展的生命周期。Maven的生命周期是抽象的,其中的具体任务都...

多线程(3)-基于Object的线程等待与唤醒

概述在使用synchronized进行线程同步中介绍了依赖对象锁定线程,本篇文章介绍如何依赖对象协调线程。同synchronized悲观锁一样,线程本身不能等待与唤醒,也是需要对象才能完成等待与唤醒的...

jquery mobile + 百度地图 + phonegap 写的一个"校园助手"的app

1jquerymobile+百度地图+phonegap写的一个"校园助手"的app,使用的是基于Flat-UI的jQueryMobile,请参考:https://github.com/...

Apache 服务启动不了 apache系统服务启动不了

{我是新手,从未遇到此问题,请各位大大勿喷}事由:今天早上上班突然发现公司网站出现问题。经过排查,发现是Apache出现问题。首先检查配置文件没有出问题后,启动服务发现Apache服务能启动,但是没法...

健康债和技术债都不能欠 公众号: 我是攻城师(woshigcs)

在Solr4.4之后,Solr提供了SolrCloud分布式集群的模式,它带来的主要好处是:(1)大数据量下更高的性能(2)更好扩展性(3)更高的可靠性(4)更简单易用什么时候应该使用Sol...

Eye Experience怎么用?HTC告诉你 eyebeam怎么用

IT之家(www.ithome.com):EyeExperience怎么用?HTC告诉你HTC上周除了发布HTCDesireEYE自拍机和HTCRE管状运动相机之外,还发布了一系列新的智能手机...

Android系统应用隐藏和应用禁止卸载

1、应用隐藏与禁用Android设置中的应用管理器提供了一个功能,就是【应用停用】功能,这是针对某些系统应用的。当应用停用之后,应用的图标会被隐藏,但apk还是存在,不会删除,核心接口就是Packag...

计算机软件技术分享--赠人玫瑰,手遗余香

一、Netty介绍Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。也就是说,Netty...

Gecco爬虫框架的线程和队列模型 爬虫通用框架

简述爬虫在抓取一个页面后一般有两个任务,一个是解析页面内容,一个是将需要继续抓取的url放入队列继续抓取。因此,当爬取的网页很多的情况下,待抓取url的管理也是爬虫框架需要解决的问题。本文主要说的是g...

一点感悟(一) 初识 初读感知的意思

时间过得很快,在IT业已从业了两年多。人这一辈子到底需要什么,在路边看着人来人往,大部分人脸上都是很匆忙。上海真是一个魔都,它有魅力,有底蕴,但是一个外地人在这里扎根置业,真的是举全家之力,还贷3...