Unity中的仿真絲襪渲染

2021-02-20 Unity3D遊戲開發精華教程乾貨
Stocking Rendering in Unity前言

一年前曾嘗試過這個課題,研究的比較淺,最終效果也一般。最近重新搬出這個課題研究,以期獲得更令人滿意的結果。
本文會在Unity的PBR基礎上,依據真實的物理原理嘗試對絲襪這種物品給出一種仿真的渲染方法。原理和計算方面還是比較簡單的,但是也需要一些SurfaceShader基礎。

相關工程文件下載在文末

物理層面的分析

在開始編寫Shader前,首先我們需要對絲襪這種物品在現實中的物理性質進行一個具體的分析。
絲襪有著眾多的款式,形狀、功能、材料各有不同,依據我在百度百科和各類淘寶爆款中豐富的調研,最終我選擇了其中最具有代表性,也是現今最常見的尼龍絲襪進行分析。

 

 

主要材質為 約90%聚醯胺纖維(錦綸/尼龍)+ 約10%聚氨酯纖維(氨綸)
基本上可以推測這種材質的金屬性(Metallic)為0,而光滑度(Smoothness)則根據種類不同會有較大區別。

丹尼爾值

各種絲襪的厚薄也有區別,「D」或「Denier」是指纖維的纖度單位。5D、10D幾乎透明,100D以上則幾乎不透,很好理解,請看下圖:

 

 

所以Shader中我們就以Denier值表示絲襪的厚薄程度。
機核網的相關文獻:11月2日褲襪之日,是時候研究神秘的褲襪了

纖維的特性

我們可以發現一種現象——靠近腿部中央的位置絲襪顏色較淡,並且如果絲襪夠薄甚至會顯出底下皮膚的顏色,而越靠近腿兩側絲襪顏色則會越深。絲襪由無數細小交錯的纖維組成,所以正對視線的纖維是最稀疏的,而越靠近腿部輪廓的纖維則對於觀察者來說越顯得密集。

 

 

能否通過渲染出每一根纖維實現絲襪的效果?
幾乎不可行,就像遊戲的毛髮渲染也沒有一根根渲染的一樣,實際纖維的密集程度遠超越現階段電腦遊戲的精度,雖然可以做出大概的意思,但是這些交錯的線條會產生大量摩爾紋而影響觀感。

 

 

簡單嘗試一下,效果可以說相當噁心了。不過擴大間距後似乎就接近了漁網襪的效果,當然這不是本文的目標…

摩爾紋
空間頻率相近的兩組圖案相互幹涉,會有更低頻率(更寬間距)的圖案顯示出來。
使用相機拍攝屏幕的照片和掃描圖上常常可以看見,遊戲中因為像素點有限,在光柵化過程中也會出現。

開始吧

既然基於PBR,新建一個Surface Shader文件,然後依據需求稍微刪改一下:

Shader "Custom/NewSurfaceShader" {    Properties {        _NormalTex("Normal", 2D) = "bump"{}        _Smoothness("Smoothness", Range(0,1)) = 0.5    }    SubShader {        Tags { "RenderType"="Opaque" }        CGPROGRAM        #pragma surface surf Standard fullforwardshadows        #pragma target 3.0        sampler2D _NormalTex;        struct Input {            float2 uv_NormalTex;        };        half _Smoothness;        fixed4 _Color;        void surf (Input IN, inout SurfaceOutputStandard o) {            o.Albedo = 1;            o.Normal = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex));            o.Metallic = 0;            o.Smoothness = _Smoothness;            o.Alpha = 1;        }        ENDCG    }    FallBack "Diffuse"}

刪除了金屬度等一些不需要的屬性,加入了法線。
我們就以此為基礎,開始我們的絲襪探索之旅吧。

計算

對絲襪纖維的密集程度定義一個屬性 疏密度(Density) ,取值範圍0(幾乎沒有纖維)到 1(完全被纖維覆蓋)。依據疏密度提供顏色給Albedo通道,疏密度越高顯現更多絲襪顏色,越低則顯現皮膚顏色。

計算疏密度需要三部步驟:

丹尼爾值對整體疏密度的影響。

受絲襪良好彈性影響,需要一張灰度貼圖來控制不同區域的拉伸程度,拉伸越大疏密度越低。

受觀察者視線的影響,絲襪平面與觀察者視線角度越小時,疏密度越大。稱其為邊緣度影響。

對於這幾點,我們定義幾個屬性:

Properties{    _Denier("Denier", Range(5,120)) = 25.0    _DenierTex("Density Texture", 2D) = "black"{}    [Enum(Strong,6,Normal,12,Weak,20)] _RimPower("Rim Power", float) = 12    _SkinTint("Skin Color Tint", Color) = (1,0.9,0.8,1)    _SkinTex("Skin Color", 2D) = "white" {}    _StockingTint("Stocking Color Tint", Color) = (1,1,1,1)    _StockingTex("Stocking Color", 2D) = "white"{}}

(1),(2) 丹尼爾值與拉伸程度貼圖

float denier = (_Denier - 5) / 115;float density = denier * (1 - tex2D(_DenierTex, IN.uv_DenierTex));

_Denier的數值進行歸一化(Normalize)。_DenierTex是一張灰度貼圖,越淡的地方表示絲襪被拉伸的程度越高,其纖維越稀疏相應會顯示更多皮膚顏色。兩者相乘。

 

 

如示例,一般情況下認為膝蓋部分和大腿越靠根部的地方會拉扯的更加厲害。
_Denier 一般只在沒有_DenierTex 貼圖的情況下做調整用,使用_DenierTex 時請保證_Denier 值為最大(120)以正確還原貼圖的效果。

(3) 邊緣度的計算

有一種很常見的被稱為 Rim Lighting 的效果,簡單來說就是模型邊緣的內發光效果,簡單來說就是模型邊緣的內發光效果,我們可以參考其中計算邊緣程度的代碼。相關代碼在Unity官方文檔的Surface Shader examples中就可以找到。

根據自己的需求改寫:

float rim = pow(1 - dot(normalize(IN.viewDir), o.Normal), _RimPower / 10);

通過點乘(dot)計算視線方向和法線方向的數量積。視線方向與法線方向完全相反時,即絲襪最中心的部分,取得值為1;而最側邊視線方向與法線方向垂直時,取得值為0。因為我們定義的是邊緣度,所以應該是越靠邊緣值越大,用1與其相減獲得正確的數值。最後對數值進行了(_RimPower / 10) 次方,實際應用時,依據效果調整_RimPower,使邊緣度更符合現實的情況。

最終混合顏色

首先需要計算出皮膚顏色(SkinColor)絲襪顏色(StockingColor),很簡單不多解釋。

float4 skinColor = tex2D(_SkinTex, IN.uv_SkinTex) * _SkinTint;float4 stockingColor = tex2D(_StockingTex, IN.uv_StockingTex) * _StockingTint;

依據疏密度對這兩個顏色進行簡單的混合,疏密度越高顯現更多絲襪顏色,越低則顯現皮膚顏色,很簡單的lerp即可。

lerp(skinColor, stockingColor, density);

完整Shader

Shader "Custom/Stocking" {    Properties{        _Denier("Denier", Range(5,120)) = 25.0        _DenierTex("Density Texture", 2D) = "black"{}        [Enum(Strong,6,Normal,12,Weak,20)] _RimPower("Rim Power", float) = 12        _SkinTint("Skin Color Tint", Color) = (1,0.9,0.8,1)        _SkinTex("Skin Color", 2D) = "white" {}        _StockingTint("Stocking Color Tint", Color) = (1,1,1,1)        _StockingTex("Stocking Color", 2D) = "white"{}        _NormalTex("Normal", 2D) = "bump"{}        _Smoothness("Smoothness", Range(0,1)) = 0.1    }    SubShader{        Tags{ "RenderType" = "Opaque" }        CGPROGRAM        #pragma surface surf Standard fullforwardshadows        #pragma target 3.0        struct Input {            float2 uv_SkinTex;            float2 uv_StockingTex;            float2 uv_DenierTex;            float2 uv_NormalTex;            float3 viewDir;        };        float _RimPower;        float _Denier;        float _Smoothness;        float4 _SkinTint;        float4 _StockingTint;        sampler2D _DenierTex;        sampler2D _SkinTex;        sampler2D _StockingTex;        sampler2D _NormalTex;        void surf(Input IN, inout SurfaceOutputStandard o) {            o.Normal = UnpackNormal(tex2D(_NormalTex, IN.uv_NormalTex));            float4 skinColor = tex2D(_SkinTex, IN.uv_SkinTex) * _SkinTint;            float4 stockingColor = tex2D(_StockingTex, IN.uv_StockingTex) * _StockingTint;            float rim = pow(1 - dot(normalize(IN.viewDir), o.Normal), _RimPower / 10);            float denier = (_Denier - 5) / 115;            float density = max(rim, (denier * (1 - tex2D(_DenierTex, IN.uv_DenierTex))));                        o.Albedo = lerp(skinColor, stockingColor, density);            o.Metallic = 0;            o.Smoothness = _Smoothness;        }    ENDCG    }    FallBack "Diffuse"}

後處理屏幕空間次表面散射 (SSSSS)

SSSSS你可能不知道,但是SSS你應該聽過,就是常說的3S材質。這個是在Github上找到的,利用屏幕空間實現的SSS效果。
最後使用Unity官方的Post Processing Stack對畫面進行調整也必不可少的。

連結:
Custom Phase Screen-Space Subsurface Scattering
Post Processing Stack

最終效果

 

 

項目文件

在我的Coding上:
https://coding.net/u/gypsum/p/Unity-UltraStocking/git
裡面還有一個2B夏裝的模型,本來是自己準備調一下效果的,後來覺得麻煩算了...

作者:石膏 來源:簡書

相關焦點

  • unity 半透明渲染技巧(3則)
    unity 半透明渲染技巧(1):固定深度法半透明渲染排序問題 長期在各種3d引擎存在,這裡將一些針對性技巧。
  • Unity腳本生命周期
    作為一個unity從業人員,相信許多人有時候都會記不住unity的生命周期,記不住沒關係,有了這張圖,一切都是浮雲。
  • 《卡通湖水渲染思路》與《Unity Shader ScreenPos》詳解
    它通過在Camera上添加Camera Depth Texture Mode腳本來實現對屏幕空間的深度採樣,輸出一張全屏大小的Render Texture我們獲取到的深度圖大致如下,深度接近零的被渲染為白色,接近一的被渲染為黑色。這裡的深度並非是線性的,關於其中公式算法的內容可以自行百度,注意到這些,我們需要在代碼中手動改為線性深度,這方便我們進行操作與計算。
  • 【渲染流程】Cluster_Unity實現概述!
    在這篇文章中,我將介紹如何用Unity 在前向渲染的基礎上,實現一套完整的Cluster 流程。
  • Unity 最新版本迎來260項功能改進,高清渲染管線HDRP升級
    大家現在就可以正式使用起高清渲染管線了。高清渲染管線的目標平臺是高端PC、Mac和遊戲主機(Xbox One和PlayStation 4),可製作高清晰度的逼真圖像。而在2019.3中的7.2.0版本中,HDRP已經由驗證。管線的設計遵循三個原則:渲染基於真實物理。
  • 在 Unity 中實現體積光渲染
    在實時渲染中這樣的效果常稱為體積光 (Volumetric Light),有時也稱作上帝光 (god light)。本文將簡單介紹體積光散射效果的相關原理,及在 Unity SRP 下的簡單實現。右圖是 HG phase function 在不同的 g 參數下的極坐標分布圖,在 g 接近 1 時表現為逆光方向上散射光強度較大Real-time Volumetric RenderingVolumetric Data體積渲染中材質的一個重要屬性是 transmittance 函數中的衰減率 σt,我們可以假定整個材質中 σt 是均等的,例如在勻質膠體中。
  • Unity中的GUI編程
    GUiText組件可被用於渲染單個的文本元素,GUITexture組件可以被用於渲染2D材質到屏幕.GUIText和GUITexture都適用於為你的遊戲繪製GUI元素(就像HUD),但這些組件不適用於在遊戲中繪製菜單.對於遊戲中的菜單(像等級選擇和選項設置頁面)你應該使用GUI和GUILayout類.
  • Unity 重Built-in到URP函數對應整理
    Unity 的可編程渲染管線(Scriptable Render Pipeline) 代表了Unity處理圖形方式的一大進步,為用戶提供了更多定製管線的能力
  • Unity中的後期處理:PostProcessing
    相信絕大多數開發者都有看過那些炫酷的效果而躍躍欲試卻又對Shader代碼望而卻步的經歷,新版本的unity新增的PostProcessing組件,讓許多人看到了希望,雖然效果數量有限,卻對於開發來說有很大的收益。
  • Unity3D 實用技巧 - Unity Shader 匯總式學習·初探篇
    渲染管線也稱為渲染流水線,是顯示晶片內部處理圖形信號相互獨立的並行處理單元。Gpu流水線上一些可高度編程的階段,而由著色器編譯出來的最終代碼是會在Gpu上運行的;有一些特定類型的著色器,如頂點著色器,片元著色器等。依靠著色器我們可以控制流水線中的渲染細節,例如用頂點著色器來進行頂點變換及傳遞數據,用片元著色器來進行逐像素渲染。
  • 在智能駕駛的開發中,為什麼仿真的作用越來越重要?
    實際上仿真技術從計算機誕生之初就已廣泛應用在現代工業化大生產的體系中了,從計算機輔助設計(CAD)、計算機輔助工程分析(CAE)到計算機輔助生產製造(CAM),虛擬仿真技術在其中都起到了非常重要的作用。
  • Unity編碼篇 Rigidbody類
    Rigidbogy組件可以使遊戲對象在物理系統的控制下運動 , 發射一顆子彈 ,如果子彈沒有命中任何物體 ,最後 子彈會因為重力落下 , 此時就可以利用 Rigidbody 組件實現 , 但是使用該組件一般在 FixedUpdate 函數中執行 , 因為物理仿真一般都在固定頻率下進行計算的 .
  • Unity自帶函數說明
    在unity的腳本中,有大量的腳本執行按照預先確定的順序執行的事件函數,由於篇幅有限,無法一一列舉,這裡只列舉一些常用的自帶函數的順序,並論述他們之間的區別
  • 在Unity Shader中自定義材質面板
    從渲染效果的角度來說並不是一個正常的shader。如果有興趣,也可以詳細閱讀Unity的官方文檔:https://docs.unity3d.com/2019.3/Documentation/ScriptReference
  • Unity PBR Standard Shader 實現詳解 (三)
    函數裡面有兩個分支,第一個分支是延遲渲染路徑。由於日常使用還是前向渲染多,所以我們先跳過。不過這裡UnityGlobalIllumination()有一個函數重載的方法,就是不同的變量數量和類型導向不同的函數分支。可以了解一下。
  • 控制renderQueue解決NGUI與Unity3D物體渲染順序問題
    今天嘗試了另一種思路來解決這一問題:直接控制Unity中特效的render queue值,來達到使得UI、特效按照我們希望的順序進行渲染的目的。.\03.客戶端\frank\UISandwichWithParticle.zip測試場景為Assets\TestScene.unity。另外這一工程中也包含了一個以鄧老師的方式解決這一問題的示例,場景為Assets\TestScene_orig.unity。首先參考一下鄧老師方法中實現這類疊加後的Draw Call細節:
  • Unity Standard Shader 技術分析
    如果我們想要寫出比Unity Standard Shader更好的Shader,或者想要選擇比Unity提供的標準渲染方案更好的渲染方案,那麼擺在我們面前的第一個問題便是Unity Standard Shader到底做了什麼?它使用了哪些技術?它又是如何應用這些技術的?事實上這是一篇寫給自己的總結,而不是寫給新人看的教程。
  • Unity推出的AR Foundation能幫助使用者解決AR開發過程中遇到的難題
    打開APP Unity推出的AR Foundation能幫助使用者解決AR開發過程中遇到的難題 工程師青青 發表於 2018-09-11 09:51:00
  • 程序丨Unity渲染基礎系列教程(七):陰影
    所以渲染陰影是非常昂貴的!渲染到深度貼圖當啟用平行光陰影的渲染選項的時候,Unity開始在渲染過程加入一個深度渲染Pass。所得到的結果會放一張與屏幕解析度相匹配的紋理貼圖中去。這個深度渲染Pass會渲染整個場景,但是僅記錄每個片段的深度信息。這是與圖形處理器用來確定片段與先前渲染的片段的深度相對關係相同的信息。
  • Unity工業專欄|Unity雲渲染技術為移動端打開新世界的大門
    雖然他們也需要雲端渲染,但有些是需要私有雲,有些是需要單獨的計費方式,有些是有特定的用戶。總之,現在雲遊戲的這種業務模式並不能滿足所有客戶的需要,特別是在工業領域。在這種情況下,也就催生了Unity的雲渲染技術。