unity urp 实现衣服上面片的效果
在看模特秀的时候,有时候身上会有那种布灵布灵的面片效果。下面两张是我在丝绸的时装秀里面截取的两张模糊图,凑合看一下吧。 如果需要在渲染里面去实现这种效果,该如何实现呢。 当然用我上一篇说到的泰森多边形了 https://blog.csdn.net/qq_30100043/article/details/127386983
我们可以使用其进行noise扰动,实现面片分离的效果,然后再使用其的id的值转换成为法向,相同片的法向相同,但由于id不同,每个片生成的法向的朝向也不相同,然后用这个法向去做高光效果,你就会得到一个亮片一个亮片的效果。 就比如这种效果,心动了吗,那么,接下来进入实战。
首先,就是用voronoi生成法向。
//亮片 float2 idvoronoi = 0; float2 uvvoronoi = 0; voronoi(input.uv * _FlakesScale, _FlakesAngle, idvoronoi, uvvoronoi, 0); half3 voronoiNormal = half3(idvoronoi * _FlakesRange, 1.0); //使用voronoi函数生成的id作为扰动法向的xy half3 noiseNormalWS = TransformTangentToWorld(voronoiNormal, tangentToWorld); //转换到世界空间坐标系下面 noiseNormalWS = NormalizeNormalPerPixel(noiseNormalWS);如果需要voronoi函数,去上一篇去拿即可。
我们还需要一个亮片的颜色,在这里,我们刚好可以使用voronoi生成的uv去随便拾取一张多颜色亮片,然后和一个颜色相乘,还可以控制强度,相乘的颜色如果为0,那么亮片将不显示。
half3 flakesBaseColor = _FlakesColor * SAMPLE_TEXTURE2D(_FlakesMap, sampler_FlakesMap, uvvoronoi).rgb;接下来就是重头戏了,数据都完成了,需要生成亮片的函数了:
half3 flakesColor = FlakesSpecular(_FlakesRoughness1, _FlakesRoughness2, _FlakesRoughnessMix, flakesBaseColor, positionWS, noiseNormalWS, viewDirWS, shadowCoord, shadowMask);这里用了两个粗糙度,然后进行混合出来的,给亮片增加了过渡的效果。计算出来的结果和最终颜色相加即可。
接下来,将函数的实现代码粘贴出来,就是主光源和附加光源进行亮片计算即可。
//实现双层的GGX,这个专门是亮片使用的,F项就不需要根据视角进行过渡的效果了 float3 DualSpecularGGXFlakes(float Lobe0Roughness, float Lobe1Roughness, float LobeMix, float3 SpecularColor, float NoH, float NoV, float NoL) { float Lobe0Alpha2 = Pow4(Lobe0Roughness); float Lobe1Alpha2 = Pow4(Lobe1Roughness); float AverageAlpha2 = Pow4((Lobe0Roughness + Lobe1Roughness) * 0.5); // Generalized microfacet specular float D = lerp(D_GGX_UE4(Lobe0Alpha2, NoH), D_GGX_UE4(Lobe1Alpha2, NoH), 1.0 - LobeMix); float Vis = Vis_SmithJointApprox(AverageAlpha2, NoV, NoL); float3 F = SpecularColor; return(D * Vis) * F; } //亮片的实现函数 float3 FlakesSpecular(float Lobe0Roughness, float Lobe1Roughness, float LobeMix, float3 SpecularColor, float3 WorldPos, float3 N, float3 V, float4 shadowCoord, float4 shadowMask) { //主光源 half3 DirectLighting_MainLight = half3(0, 0, 0); { Light mainLight = GetMainLight(shadowCoord, WorldPos, shadowMask); half3 L = mainLight.direction; half3 LightColor = mainLight.color; half Shadow = mainLight.shadowAttenuation; float3 H = normalize(L + V); float NoH = saturate(dot(N, H)); float NoV = saturate(abs(dot(N, V)) + 1e-5); float NoL = saturate(dot(N, L)); float3 Radiance = NoL * LightColor * Shadow * PI; DirectLighting_MainLight = DualSpecularGGXFlakes(Lobe0Roughness, Lobe1Roughness, LobeMix, SpecularColor, NoH, NoV, NoL) * Radiance; } //附加光源 half3 DirectLighting_AddLight = half3(0, 0, 0); #ifdef _ADDITIONAL_LIGHTS uint pixelLightCount = GetAdditionalLightsCount(); for (uint lightIndex = 0; lightIndex < pixelLightCount; ++lightIndex) { Light light = GetAdditionalLight(lightIndex, WorldPos, shadowMask); half3 L = light.direction; half3 LightColor = light.color; half Shadow = light.shadowAttenuation * light.distanceAttenuation; float3 H = normalize(L + V); float NoH = saturate(dot(N, H)); float NoV = saturate(abs(dot(N, V)) + 1e-5); float NoL = saturate(dot(N, L)); float3 Radiance = NoL * LightColor * Shadow * PI; DirectLighting_AddLight += DualSpecularGGXFlakes(Lobe0Roughness, Lobe1Roughness, LobeMix, SpecularColor, NoH, NoV, NoL) * Radiance; } #endif return DirectLighting_MainLight + DirectLighting_AddLight; }