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

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

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

本篇文章我们一起来学习下如何用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到此就结束了,我们下期再见!

相关推荐

IDEA界面太丑??尝试一下这几个插件

前言IntelliJIDEA主要用于支持Java、Scala、Groovy等语言的开发工具,同时具备支持目前主流的技术和框架,擅长于企业应用、移动应用和Web应用的开发。IntelliJi...

小巧 Vue 页面滚动进度条组件ScrollProgress

今天给大家分享一个轻量级Vue.js全屏滚动进度条组件VueScrollProgress。vue-scroll-progress一款基于vue.js构建的页面滚动进度条组件,...

基于vue实现可视化拖拽编辑器,页面生成工具,提升前端开发效率

项目介绍基于vue实现的可视化拖拽编辑器,实现页面生成工具,提升前端开发效率。可以基层到移动端项目作为自定义json直接生成UI页面。项目特点功能特点...

优秀 vue+heyui 后端管理系统HeyUI-Admin

今天再给小伙伴们推荐一款成熟的企业中后台管理系统HEYUI-Admin。heyui-admin基于vue.js和heyui组件库构建的后台管理系统。包含基础表单/表格功能,拓展组件(图表、富文本编辑...

响应式 Vue.js 前端组件化框架Xvue-UI

今天给小伙伴们推荐一款超不错的Vue轻量级组件框架XVueUI。xvue-ui基于vue2.x构建的响应式前端组件化框架。轻量级、易于上手,提供...

《基于SpringBoot+Vue的在线视频系统设计与实现》开题报告

【计算机毕业设计案例】基于SpringBoot+Vue的在线视频系统设计与实现_哔哩哔哩_bilibili...

超好用 Vue.js 图片裁切组件Vue-ImgCutter

今天给小伙伴们分享一个超棒的Vue图片任意裁剪插件VueImgCutter。vue-img-cutter基于vue2.x构建的轻量级剪切图片组件。支持移动图像、放大缩小图片、任意移动图片、固定比...

Vue 3 进阶用法:异步组件(vue 异步组件原理)

一、代码分割一个大型前端应用,如果所有代码都放在单一文件,体积会特别大,下载时间长,白屏时间久,用户体验差。...

源码补丁神器—patch-package(源码助手怎么用)

作者:张浩一、背景vue项目中使用vue-pdf第三方插件预览pdf,书写业务代码完美运行,pdf文件内容正常预览无问题。后期需求有变,业务需求增加电子签章功能。这个时候pdf文件的内容可以显示出...

经验分享:Vue2 项目升级 Vue3 + Element Plus,借助Deepseek手动升级

Vue3出来好久了,我开发的项目还在使用Vue2框架,一般情况下不考虑升级,但是最近需要接入工作流程引擎之类的,看了下Vue2生态下操作空间不是很好,那索性尝试升级Vue3吧。一番操作下来,升级成功,...

34K Star!史上最全JavaScript资源库 awesome-javascript

34KStar!史上最全JavaScript资源宝库大揭秘引言在GitHub上,有一个备受瞩目的JavaScript资源仓库,以其全面的内容和精心的分类,成为了众多开发者的必备参考。这个拥有超过...

基于 Vue.js 磁片栅格布局组件VueGridLayout

#头条创作挑战赛#今天给大家分享一个超优秀的vue.js拖拽栅格布局插件VueGridLayout。...

6款高颜值 Vue3 PC端UI组件库(vue3开发组件库)

马上到国庆了,还没学习或者想学习vue3的小伙伴们有安排上没?这次推荐几个比较流行的VUE3UI组件库,合理利用,又或者学习借鉴都是不错的选择。1、element-pluselement-plus...

高性能 vue.js+ztree 树形组件Vue-GiantTree

今天给大家分享一款超棒的Vue海量数据渲染树形组件VueGiantTree。vue-giant-tree基于ztree封装的Vue树形组件。轻松实现大数据高性能渲染,适合海量数据渲染场景。zTr...

【推荐】2024年推荐的6款开源免费 Vue 后台管理系统模板,建议收藏

前言在现今的软件开发领域,...