您當前的位置:首頁 > 遊戲

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

作者:由 流朔 發表于 遊戲時間:2020-09-10

0。 前言

眾所周知,在卡通渲染領域有著許許多多的Trick,而角色的臉部作為阿宅們關注的重點,Trick自然也少不了。

在各種插畫、動畫中,角色頭髮常常在角色臉上投下與頭髮形狀相似的陰影,但這個效果如果使用ShadowMap來實現的話,對ShadowMap的精度要求大到有些不現實。

一些例圖:

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

筆者近日在實現卡渲時發現,如果沒有這個劉海投影,總感覺還差點味。

此時的臉部陰影是使用軟陰影做的,可見效果並不是很好

而使用本文所講述的方法可以產生較好的效果:

(臉部法線筆者並沒有進行球形對映或者其他什麼修改,所以普通的自陰影部分或許有些不美觀,各位只關注劉海投影即可)

0。1 效果實現原理

其實效果的原理相當簡單,簡略來說就一句話:

首先生成一個頭發的純色buffer,然後在渲染角色臉部的時候對這個純色buffer做取樣取得陰影區域即可。

本文目錄為:

使用Render Feature生成純色buffer

渲染臉部時對這個純色buffer進行取樣

一些改良

美中不足

結語

Github範例工程連結

本文內容不多,因為只涉及這一個細節效果,不會談及整個卡渲模型,如果讀者對整體的卡通渲染模型,推薦閱讀:

0。2 注

筆者所用Unity版本為2019。4。6f1,URP 7。3。1

筆者經驗甚少,才淺學疏,難以避免文中出現錯誤,還請大家不吝斧正,只求輕噴。

1。 使用Render Feature生成純色buffer

以Create/Rendering/URP/Render Feature新建一個Render Feature。

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

開啟之後能看見是這樣的:

using

UnityEngine

using

UnityEngine。Rendering

using

UnityEngine。Rendering。Universal

public

class

CelHairShadow_Test

ScriptableRendererFeature

{

class

CustomRenderPass

ScriptableRenderPass

{

// This method is called before executing the render pass。

// It can be used to configure render targets and their clear state。 Also to create temporary render target textures。

// When empty this render pass will render to the active camera

render target

// You should never call CommandBuffer。SetRenderTarget。 Instead call ConfigureTarget and ConfigureClear

// The render pipeline will ensure target setup and clearing happens in an performance manner。

public

override

void

Configure

CommandBuffer

cmd

RenderTextureDescriptor

cameraTextureDescriptor

{

}

// Here you can implement the rendering logic。

// Use ScriptableRenderContext to issue drawing commands or execute command buffers

// https://docs。unity3d。com/ScriptReference/Rendering。ScriptableRenderContext。html

// You don‘t have to call ScriptableRenderContext。submit, the render pipeline will call it at specific points in the pipeline。

public

override

void

Execute

ScriptableRenderContext

context

ref

RenderingData

renderingData

{

}

/// Cleanup any allocated resources that were created during the execution of this render pass。

public

override

void

FrameCleanup

CommandBuffer

cmd

{

}

}

CustomRenderPass

m_ScriptablePass

public

override

void

Create

()

{

m_ScriptablePass

=

new

CustomRenderPass

();

// Configures where the render pass should be injected。

m_ScriptablePass

renderPassEvent

=

RenderPassEvent

AfterRenderingOpaques

}

// Here you can inject one or multiple render passes in the renderer。

// This method is called when setting up the renderer once per-camera。

public

override

void

AddRenderPasses

ScriptableRenderer

renderer

ref

RenderingData

renderingData

{

renderer

EnqueuePass

m_ScriptablePass

);

}

}

對32行及之後的內容我們基本可以忽略,只需要修改一處:

將第39行的renderPassEvent從AfterRenderingOpaques修改為BeforeRenderingOpaques

這是為了保證我們在渲染臉部時,這個純色Buffer已存在。

然後在類的開頭寫上我們的Setting:

[System。Serializable]

public

class

Setting

{

//標記頭髮模型的Layer

public

LayerMask

hairLayer

//標記臉部模型的Layer

public

LayerMask

faceLayer

//Render Queue的設定

[Range(1000, 5000)]

public

int

queueMin

=

2000

[Range(1000, 5000)]

public

int

queueMax

=

3000

//使用的Material

public

Material

material

}

public

Setting

setting

=

new

Setting

();

然後到Setting

資料夾

(預設的Rendering List所存放的路徑)下,

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

透過Add Render Feature新增我們剛剛新建的這個Render Feature

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

現在是這樣的,我們可以先新增Layer

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

並設定Hair Layer和Face Layer

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

(實際上眼睛也需要設定為Face,這些沒意思的圖就不多發了)

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

我們先把Material給填上吧,

新建一個Shader——由於URP沒有預設的Shader模板,因此在此我給各位提供一個網上找來的模板,直接複製貼上覆蓋原有的Shader即可

// Example Shader for Universal RP

// Written by @Cyanilux

// https://cyangamedev。wordpress。com/urp-shader-code/

Shader

“Custom/UnlitShaderExample”

{

Properties

{

_BaseMap

“Example Texture”

2

D

=

“white”

{}

_BaseColor

“Example Colour”

Color

=

0

0。66

0。73

1

}

SubShader

{

Tags

{

“RenderType”

=

“Opaque”

“RenderPipeline”

=

“UniversalRenderPipeline”

}

HLSLINCLUDE

#include

“Packages/com。unity。render-pipelines。universal/ShaderLibrary/Core。hlsl”

CBUFFER_START

UnityPerMaterial

float4

_BaseMap_ST

float4

_BaseColor

CBUFFER_END

ENDHLSL

Pass

{

Name

“Example”

Tags

{

“LightMode”

=

“UniversalForward”

}

HLSLPROGRAM

#pragma vertex vert

#

pragma fragment frag

struct

a2v

{

float4

positionOS

POSITION

float2

uv

TEXCOORD0

float4

color

COLOR

};

struct

v2f

{

float4

positionCS

SV_POSITION

float2

uv

TEXCOORD0

float4

color

COLOR

};

TEXTURE2D

_BaseMap

);

SAMPLER

sampler_BaseMap

);

v2f

vert

a2v

v

{

v2f

o

//VertexPositionInputs positionInputs = GetVertexPositionInputs(input。positionOS。xyz);

//o。positionCS = positionInputs。positionCS;

// Or this :

o

positionCS

=

TransformObjectToHClip

v

positionOS

xyz

);

o

uv

=

TRANSFORM_TEX

v

uv

_BaseMap

);

o

color

=

v

color

return

o

}

half4

frag

v2f

i

SV_Target

{

half4

baseMap

=

SAMPLE_TEXTURE2D

_BaseMap

sampler_BaseMap

i

uv

);

return

baseMap

*

_BaseColor

*

i

color

}

ENDHLSL

}

}

}

以其新建Material並拖到RenderFeature裡

那麼回想一下我們的步驟,我們要用Render Feature渲染一個頭發純色圖,那麼很簡單,我們只需要Return (1,1,1,1)即可,反正只要個顏色嘛!

是嗎?

當然沒有這麼簡單。如果只是這樣畫的話,就會繪製角色頭後面的頭髮,無法拿到劉海的形狀。

我們之前設定了一個Face Layer,就是為了

在繪製頭髮純色之前,先寫入臉部的深度

,之後在繪製頭髮時進行深度測試即可獲取劉海形。

如此便說明我們要在這個Shader中寫兩個Pass,一個是給臉部寫入深度用,一個是繪製頭髮用的,兩個都很簡單

Shader

“Custom/HairShadowSoild_Test”

{

SubShader

{

Tags

{

“RenderType”

=

“Opaque”

“RenderPipeline”

=

“UniversalRenderPipeline”

}

HLSLINCLUDE

#include

“Packages/com。unity。render-pipelines。universal/ShaderLibrary/Core。hlsl”

ENDHLSL

Pass

{

Name

“FaceDepthOnly”

Tags

{

“LightMode”

=

“UniversalForward”

}

ColorMask

0

ZTest

LEqual

ZWrite

On

HLSLPROGRAM

#pragma vertex vert

#pragma fragment frag

struct

a2v

{

float4

positionOS

POSITION

};

struct

v2f

{

float4

positionCS

SV_POSITION

};

v2f

vert

a2v

v

{

v2f

o

//VertexPositionInputs positionInputs = GetVertexPositionInputs(input。positionOS。xyz);

//v。positionCS = positionInputs。positionCS;

// Or this :

o

positionCS

=

TransformObjectToHClip

v

positionOS

xyz

);

return

o

}

half4

frag

v2f

i

SV_Target

{

return

0

0

0

1

);

}

ENDHLSL

}

Pass

{

Name

“HairSimpleColor”

Tags

{

“LightMode”

=

“UniversalForward”

}

Cull

Off

ZTest

LEqual

ZWrite

Off

HLSLPROGRAM

#pragma vertex vert

#pragma fragment frag

struct

a2v

{

float4

positionOS

POSITION

};

struct

v2f

{

float4

positionCS

SV_POSITION

};

v2f

vert

a2v

v

{

v2f

o

VertexPositionInputs

positionInputs

=

GetVertexPositionInputs

v

positionOS

xyz

);

o

positionCS

=

positionInputs

positionCS

// Or this :

//o。positionCS = TransformObjectToHClip(v。positionOS。xyz);

return

o

}

half4

frag

v2f

i

SV_Target

{

return

float4

1

1

1

1

);

}

ENDHLSL

}

}

}

那麼讓我們把這個Material擱一旁,開始正式動工Render Feature吧

在CustomRenderPass類中新增以下內容

//用於儲存之後申請來的RT的ID

public

int

soildColorID

=

0

public

ShaderTagId

shaderTag

=

new

ShaderTagId

“UniversalForward”

);

public

Setting

setting

FilteringSettings

filtering

FilteringSettings

filtering2

//新的構造方法

public

CustomRenderPass

Setting

setting

{

this

setting

=

setting

//建立queue以用於兩個FilteringSettings的賦值

RenderQueueRange

queue

=

new

RenderQueueRange

();

queue

lowerBound

=

Mathf

Min

setting

queueMax

setting

queueMin

);

queue

upperBound

=

Mathf

Max

setting

queueMax

setting

queueMin

);

filtering

=

new

FilteringSettings

queue

setting

faceLayer

);

filtering2

=

new

FilteringSettings

queue

setting

hairLayer

);

}

前面的每個變數我們之後都會用到,目前還只是宣告一下。

然後給CustomRenderPass建立一個構造方法,其中便使用到我們的Setting

此時會有一個報錯,因為下面在構造CustomRenderPass時用的還是無參的老方法,加個setting即可。

FilteringSettings筆者個人理解為一個

過濾器

,更直白來說,它可以幫助我們選擇我們想要渲染的物體,

而為了選擇物體,我們需要加一些條件,比如這個(些)物體的Render Queue,Layer等,這樣Unity就會找到符合這些條件的物體,並用於之後的渲染。

public

override

void

Configure

CommandBuffer

cmd

RenderTextureDescriptor

cameraTextureDescriptor

{

//獲取一個ID,這也是我們之後在Shader中用到的Buffer名

int

temp

=

Shader

PropertyToID

“_HairSoildColor”

);

//使用與攝像機Texture同樣的設定

RenderTextureDescriptor

desc

=

cameraTextureDescriptor

cmd

GetTemporaryRT

temp

desc

);

soildColorID

=

temp

//將這個RT設定為Render Target

ConfigureTarget

temp

);

//將RT清空為黑

ConfigureClear

ClearFlag

All

Color

black

);

}

public

override

void

Execute

ScriptableRenderContext

context

ref

RenderingData

renderingData

{

var

draw1

=

CreateDrawingSettings

shaderTag

ref

renderingData

renderingData

cameraData

defaultOpaqueSortFlags

);

draw1

overrideMaterial

=

setting

material

draw1

overrideMaterialPassIndex

=

0

context

DrawRenderers

renderingData

cullResults

ref

draw1

ref

filtering

);

var

draw2

=

CreateDrawingSettings

shaderTag

ref

renderingData

renderingData

cameraData

defaultOpaqueSortFlags

);

draw2

overrideMaterial

=

setting

material

draw2

overrideMaterialPassIndex

=

1

context

DrawRenderers

renderingData

cullResults

ref

draw2

ref

filtering2

);

}

RenderTextureDescriptor的設定其實可以更自定義,比如只用螢幕一半

解析度

的Texture,或者用RGB565之類,這裡就不展開講了。

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

此時使用Frame Debugger即可看到我們所渲染出的這個buffer(因為這個凱露模型的衣服和頭髮是同一網格,便使用同一材質,所以我們能在buffer中看見衣服也被渲染了)。

那麼,讓我們進入下一個階段吧。

2。 渲染臉部時對這個純色buffer進行取樣

筆者雖然自己在URP實現了Cel Shading,奈何本人學疏才淺,Shader寫得有些不堪入目、雜亂無章,不適合拿出來給各位展示(2021。01。15更新Github工程,可見文末),因此筆者就提供一些資料供各位參考,還請見諒。

一是前文所分享的2173大佬的文

還有一個不得不提的當然是Colin大佬的URP Toon Lit Shader:

如果你並不會書寫URP中的Shader,筆者建議學習此文:

————————————————-

那麼讓我們迴歸正題,

既然我們已經擁有繪製了頭髮的buffer,要怎麼取樣它呢?

本文的方法是使用View Space的Light Direction

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

比如當環境的

光照方向

是這樣時,便會有如紅箭頭方向的取樣,

得到的就會是與下圖類似的結果。

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

我在Shader中聲明瞭Keyword “_IsFace”,來標記是否為臉,產生Shader的變體(Variant)

struct

v2f

{

float4

positionCS

SV_POSITION

float2

uv

TEXCOORD0

float3

positionWS

TEXCOORD1

float3

normal

TEXCOORD2

#if _IsFace

float4

positionSS

TEXCOORD3

#endif

};

TEXTURE2D

_HairSoildColor

);

SAMPLER

sampler_HairSoildColor

);

v2f

vert

a2v

v

{

v2f

o

//……

#if _IsFace

o

positionSS

=

ComputeScreenPos

positionInputs

positionCS

);

#endif

//……

return

o

}

half4

frag

v2f

i

SV_Target

{

//……

//face shadow

#if _IsFace

//計算該畫素的Screen Position

float2

scrPos

=

i

positionSS

xy

/

i

positionSS

w

//獲取螢幕資訊

float4

scaledScreenParams

=

GetScaledScreenParams

();

//計算View Space的光照方向

float3

viewLightDir

=

normalize

TransformWorldToViewDir

mainLight

direction

));

//計算取樣點,其中_HairShadowDistace用於控制取樣距離

float2

samplingPoint

=

scrPos

+

_HairShadowDistace

*

viewLightDir

xy

*

float2

1

/

scaledScreenParams

x

1

/

scaledScreenParams

y

);

//若取樣點在陰影區內,則取得的value為1,作為陰影的話還得用1 - value;

float

hairShadow

=

1

-

SAMPLE_TEXTURE2D

_HairSoildColor

sampler_HairSoildColor

samplingPoint

)。

r

//將作為二分色依據的ramp乘以shadow值

ramp

*=

hairShadow

#else

//若不是臉,直接將ramp乘以Shadow map的取樣值,Shadow map的計算在此文非重點,姑且略過

ramp

*=

shadow

#endif

//……

}

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

此時調整_HairShadowDistace的值便可獲得類似如此的結果

基本的效果已經有了,但是還是存在一點問題。

3。 一些改良

3。1 以NDC。w調整取樣距離

如果只是這樣取樣的話,會發現如果攝像機離人物的臉比較近,則陰影取樣距離會顯得很小,如圖

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

而離得遠了,取樣距離又會顯得很大

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

顯然,這並不是我們想看到的結果,那要如何調整呢?

讀者是否覺得這種情況似曾相識?

沒錯,瞭解卡通渲染的朋友可能會覺得這跟解決Back facing描邊粗細的問題十分相似,

使用NDC來調整back facing描邊,是一大著名的方案。

只是back facing描邊存在的問題是“

近大遠小

”,而我們這個是“近小遠大”。

不妨試試看?

struct

v2f

{

//……

#if _IsFace

float4

positionSS

TEXCOORD3

float

posNDCw

TEXCOORD4

#endif

//……

};

v2f

vert

a2v

v

{

//……

#if _IsFace

o

posNDCw

=

positionInputs

positionNDC

w

o

positionSS

=

ComputeScreenPos

positionInputs

positionCS

);

#endif

//……

return

o

}

half4

frag

v2f

i

SV_Target

{

//……

#if _IsFace

float2

scrPos

=

i

positionSS

xy

/

i

positionSS

w

float4

scaledScreenParams

=

GetScaledScreenParams

();

//在Light Dir的基礎上乘以NDC。w的倒數以修正攝像機距離所帶來的變化

float3

viewLightDir

=

normalize

TransformWorldToViewDir

mainLight

direction

))

*

1

/

i

posNDCw

float2

samplingPoint

=

scrPos

+

_HairShadowDistace

*

viewLightDir

xy

*

float2

1

/

scaledScreenParams

x

1

/

scaledScreenParams

y

);

float

hairShadow

=

1

-

SAMPLE_TEXTURE2D

_HairSoildColor

sampler_HairSoildColor

samplingPoint

)。

r

ramp

*=

hairShadow

#else

//……

}

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

此時我們在較近距離也可以看到明顯的陰影區域了

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

中等距離也能看見較為明顯的陰影

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

較遠時陰影就會消隱至看不見了。

那麼這一問題也基本解決了。

當然,這並不是最完美的方法,或許美術會覺得近處陰影範圍太大,中等距離陰影範圍太小等……可以在這個基礎上用曲線函式再調整一下采樣距離,精益求精,爭取獲得最好的效果。

3。2 解決特定角度錯誤取樣的問題

當我們攝像機的角度與臉的正前方基本垂直時,我們會發現角色臉部出現了非常詭異的現象,如圖

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

究其原因,是由於臉部錯誤取樣到了它“後面”的頭髮,而顯然這種情況是我們不願看到的。

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

如圖,臉部錯誤取樣了兩個紅框圈出的頭髮。

那咋辦呢……這也沒法深度檢測,又沒深度圖。

確實是沒有深度圖,不過我們可以手動來進行“深度檢測”。

我們只要在buffer中寫入頭髮的深度,不就可以在渲染臉部時進行兩者的深度比較了嗎?

不過……深度值咋算的來著?

深度圖

中的深度值如何計算不清楚的讀者可以參考這篇文章:

這邊直接貼出結論(在OpenGL中):

depth = POSclip。z / POSclip。w;

depth = depth * 0。5 + 0。5;

因此我們將用於RenderFeature的Shader的第二個Pass改為

Pass

{

Name

“HairSimpleColor”

Tags

{

“LightMode”

=

“UniversalForward”

}

Cull

Off

ZTest

LEqual

ZWrite

Off

HLSLPROGRAM

#pragma vertex vert

#pragma fragment frag

struct

a2v

{

float4

positionOS

POSITION

};

struct

v2f

{

float4

positionCS

SV_POSITION

};

v2f

vert

a2v

v

{

v2f

o

VertexPositionInputs

positionInputs

=

GetVertexPositionInputs

v

positionOS

xyz

);

o

positionCS

=

positionInputs

positionCS

return

o

}

half4

frag

v2f

i

SV_Target

{

float

depth

=

i

positionCS

z

/

i

positionCS

w

*

0。5

+

0。5

return

float4

1

depth

0

1

);

}

ENDHLSL

}

我們在buffer的g通道中寫入了頭髮的depth,在取樣時進行比較即可

當然,我們也要用同樣的方法計算臉部的depth值。

#if _IsFace

float2

scrPos

=

i

positionSS

xy

/

i

positionSS

w

float4

scaledScreenParams

=

GetScaledScreenParams

();

float3

viewLightDir

=

normalize

TransformWorldToViewDir

mainLight

direction

))

*

1

/

i

posNDCw

float2

samplingPoint

=

scrPos

+

_HairShadowDistace

*

viewLightDir

xy

*

float2

1

/

scaledScreenParams

x

1

/

scaledScreenParams

y

);

//新增的“深度測試”

float

depth

=

i

positionCS

z

/

i

positionCS

w

*

0。5

+

0。5

float

hairDepth

=

SAMPLE_TEXTURE2D

_HairSoildColor

sampler_HairSoildColor

samplingPoint

)。

g

//0。0001為bias,用於

精度校正

float

depthCorrect

=

depth

<

hairDepth

+

0。0001

0

1

float

hairShadow

=

1

-

SAMPLE_TEXTURE2D

_HairSoildColor

sampler_HairSoildColor

samplingPoint

)。

r

hairShadow

=

lerp

0

1

depthCorrect

);

ramp

*=

hairShadow

#else

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

————————-

2020。09。14 更新

此時可以回過頭來看shader

//這行的取樣已經不再必要

//float hairShadow = 1 - SAMPLE_TEXTURE2D(_HairSoildColor, sampler_HairSoildColor, samplingPoint)。r;

float hairShadow = lerp(0, 1, depthCorrect);

我們在下一行就用lerp(0, 1, depthCorrect)把上一行的取樣值覆蓋掉了。

究其原因,是因為r通道原本就是隻“輸出一個值以標記這個畫素是頭髮”,而我們將深度寫入g通道時,就隱性地完成了這一步。因此,我們buffer的r通道已經失去意義了。

因此我們在RenderFeature的shader將寫入r通道的部分捨去,產出一個只寫有深度的buffer即可。

相信這部分的修改各位一定都會,就不加以贅述了。

此處感謝 @迷彩大兵 ,幫我指出了這個贅餘內容,感激不盡。

同樣的,還記得我們之前哪一步跟深度有關嗎?

沒錯,之前我們有一次要寫入臉部深度,為的就是不讓臉部接收到來自“後面”的頭髮的陰影。

既然我們已經在buffer中寫入頭髮的深度了,那麼使用臉部與頭髮的深度對比,完全能夠規避這個問題,於是乎

我們Face寫入深度的那一次渲染也是可以捨棄的

。這樣一來就更加節省資源了。

或許有的朋友要問了,“那為什麼不一開始就往最合理的方向寫文章呢?你這樣讓我改來改去不是很煩”。

筆者其實不僅僅是想分享一個技術,同時也想跟大家分享一下自己創作的思路、一個想法不斷被完善的過程。

每一個想法的實現都不是一帆風順的,都會經歷數次刪刪改改,個人認為學習這一過程其實比直接學習所謂最終效果更有價值。

————————-

那麼,我們終於解決了之前的鼻樑錯誤陰影問題。

……但我們真的取得完全勝利了嗎?

4。 美中不足

上一張圖,如果讀者仔細看的話,會發現其實額頭部分的一些陰影也被消除了,如下圖紅圈圈出的部分。

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

這又是什麼原因呢?

正是因為我們消除了臉部“後面”的頭髮的投影,但是實際上這種投影是完全可能發生的。

也就是說,之前解決“

鼻樑陰影

”的方法還是過於一刀切了,將一些可能實際存在的狀況也否決,也就導致一些情況下會出錯了。

而這種情況又要怎麼解決?

或許我們要給臉部加一個Mask,比如額頭上不採用深度檢測,而鼻樑上採用。

筆者比較懶,就暫且使用positionWS了,其實原理上應該使用positionOS或者上個貼圖。

//……

float

mask

=

smoothstep

1。6

1。51

i

positionWS

y

);

float

depthCorrect

=

depth

*

mask

<

hairDepth

+

0。001

0

1

//……

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

mask

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

中途截斷的Shadow顯然不怎麼好看……

可惜筆者並沒有想到更好的方法來解決這個問題,或許畫一個Mask是

最優解

但僅是這種方法已經足以應付絕大部分情況了

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

加mask之前的效果

【Unity URP】以Render Feature實現卡通渲染中的劉海投影

加了mask之後

咱們的Mask並沒有木大,今後也要繼續做加把勁騎士。

那麼最後,放上一個燈光和人物都在轉的效果影片吧

5。 結語

在此必須要感謝某waifu群的群友們,他們肯於抽出時間回答我這種

圖形學

萌新的問題,並給出不少有意義的建議,如果沒有你們就沒有這篇文章,實在感謝。

雖然這個方法仍然存在一些瑕疵,但是個人認為已經能夠使用了,如果各位有建議的話,歡迎踴躍評論交流~

6。 Github範例工程

參考資料

https://

zhuanlan。zhihu。com/p/10

9101851

https://

github。com/ColinLeung-N

iloCat/UnityURPToonLitShaderExample

https://

cyangamedev。wordpress。com

/2020/06/05/urp-shader-code/

https://

zhuanlan。zhihu。com/p/14

4209981

https://www。

jianshu。com/p/6eb1f4501

126

標簽: 取樣  positionCS  臉部  Shader  buffer