網(wǎng)站代理備案網(wǎng)絡(luò)運營與推廣
回到目錄
Unity置換貼圖局部距離曲面細(xì)分
??大家好,我是阿趙。
??這篇文章是我無聊的時候做了一個demo,覺得挺有趣,于是就發(fā)上來。這里面包含了4個內(nèi)容:置換貼圖、頂點偏移、局部曲面細(xì)分,曲面細(xì)分按距離調(diào)整強度。
一、考慮的思路
??一開始我是在考慮不同的技術(shù)手段實現(xiàn)物體的表面凹凸效果的。我能想到的方法大概是這些:
1、凹凸貼圖
2、法線貼圖
3、視差偏移
4、置換頂點
??這幾個方法里面,凹凸貼圖現(xiàn)在很少有人用了,因為效果不好。法線貼圖效果比凹凸貼圖好很多,不過他只能模擬光射到表面時,通過不同的法線方向來模擬凹凸感覺,并不會真正的產(chǎn)生凹凸變形。視差偏移的效果比法線貼圖好很多,因為他是通過改變采樣的位置來模擬遮擋物和被遮擋物之間的關(guān)系,所以產(chǎn)生的不止是光影的凹凸感,而是會真正的有遮擋關(guān)系。不過視差偏移真的要效果逼真,要采樣多次,而且也不適合做太過明顯的變形,比如地形大面積凹凸。
??最后,就把思考點停留在了置換頂點上面了。這個是一個比較真實的手段,靠一張置換貼圖,就能生成出不同高度的模型。
二、實現(xiàn)手段
??我這里就簡單的準(zhǔn)備了一張高度圖,黑色是凹陷,白色是凸出,灰色是平地。
??通過在頂點程序采樣,根據(jù)黑白的值來偏移頂點的y軸,就可以做出凹凸的效果:
??很明顯的問題,頂點數(shù)不夠,所以凹凸的效果不好。
??這個時候,可以使用曲面細(xì)分,增加一些面數(shù),就可以看到凹凸的感覺比較正常:
??不過這么多面數(shù)并不是我想希望看到的,所以最后,再加一個根據(jù)高度圖決定是否需要細(xì)分,只有凹凸的部分做細(xì)分。這里需要讀取高度圖,然后把高度圖的0到1范圍轉(zhuǎn)換成-1到1的范圍,然后然后計算值的絕對值大于一個數(shù)值才需要細(xì)分。也可以不轉(zhuǎn)換到-1到1,就那0到1的范圍減去0.5,再去絕對值比較也想。最后,就能計算出一個范圍內(nèi)才需要細(xì)分的效果:
??這樣在需要凹凸的地方增加一些面數(shù),其他的地方還是保持正常。
??最后,我還想根據(jù)距離進行細(xì)分,如果離鏡頭遠了,那么細(xì)分的程度就沒那么大,所以把剛才的那個計算細(xì)分的值,再用UnityDistanceBasedTess方法,傳入距離的最大最小值,就可以計算出根據(jù)距離的細(xì)分結(jié)果了:
??這個效果雖然只是我一時想起來做的一個小Demo,但我覺得似乎還是在某些地方挺好用的,你們是否想到在哪些地方用得上呢?
三、Shader代碼:
Shader "azhao/DisplacementTest"
{Properties{_heightTex("heightTex", 2D) = "white" {}_heightTexVal("heightTexVal",float) = 0.01_TessValue("Max Tessellation", Range(1, 32)) = 15_normalTex("normalTex", 2D) = "white" {}_height("height", Float) = 0_displacement("displacement",float) = 1_minDist("minDist",float) = 10_maxDist("maxDist",float) = 25[HideInInspector] _texcoord( "", 2D ) = "white" {}[HideInInspector] __dirty( "", Int ) = 1}SubShader{Tags{ "RenderType" = "Opaque" "Queue" = "Geometry+0" }Cull BackCGPROGRAM#include "Tessellation.cginc"#pragma target 4.6#pragma surface surf Standard keepalpha addshadow fullforwardshadows vertex:vertexDataFunc tessellate:tessFunction struct Input{half filler;float2 uv_texcoord;};uniform sampler2D _heightTex;uniform float4 _heightTex_ST;uniform float _height;uniform float _TessValue;uniform sampler2D _normalTex;uniform float4 _normalTex_ST;float _displacement;float _heightTexVal;float _minDist;float _maxDist;float4 tessFunction(appdata_full v0, appdata_full v1, appdata_full v2){//這里要說明一下,傳進來三個點,不能直接求平均值,而要逐個點去采樣//因為只要有一個點在需要細(xì)分的范圍內(nèi),這整個網(wǎng)格就需要細(xì)分,不然凹凸的邊緣會和不需要細(xì)分的網(wǎng)格裂開float2 uv0 = v0.texcoord * _heightTex_ST.xy + _heightTex_ST.zw;float col0 = (tex2Dlod(_heightTex, float4(uv0, 0, 0.0)).r - 0.5);float2 uv1 = v1.texcoord * _heightTex_ST.xy + _heightTex_ST.zw;float col1 = (tex2Dlod(_heightTex, float4(uv1, 0, 0.0)).r - 0.5);float2 uv2 = v2.texcoord * _heightTex_ST.xy + _heightTex_ST.zw;float col2 = (tex2Dlod(_heightTex, float4(uv2, 0, 0.0)).r - 0.5);float col = max(abs(col0), abs(col1));col = max(col, abs(col2));col = step( _heightTexVal, col);col = col * _displacement;col = max(col, 0.01f);return UnityDistanceBasedTess(v0.vertex, v1.vertex, v2.vertex, _minDist, _maxDist, col);}void vertexDataFunc( inout appdata_full v ){float2 uv_heightTex = v.texcoord * _heightTex_ST.xy + _heightTex_ST.zw;float temp_output_4_0 = ( tex2Dlod( _heightTex, float4( uv_heightTex, 0, 0.0) ).r - 0.5 );float3 appendResult13 = (float3(0.0 , ( temp_output_4_0 * _height ) , 0.0));v.vertex.xyz += appendResult13;v.vertex.w = 1;}void surf( Input i , inout SurfaceOutputStandard o ){float4 color16 = IsGammaSpace() ? float4(0.5660378,0.5660378,0.5660378,0) : float4(0.280335,0.280335,0.280335,0);float2 uv_normalTex = i.uv_texcoord * _normalTex_ST.xy + _normalTex_ST.zw;o.Normal = UnpackNormal(tex2D(_normalTex, uv_normalTex));o.Albedo = color16.rgb;o.Alpha = 1;}ENDCG}Fallback "Diffuse"
}