UE5 substrate flake normal map 亚克力

发布时间 2023-10-05 22:31:58作者: 爱莉希雅

前言

本篇将运用UE5的substrate系统制作一个亚克力圆盘

效果如下
image-20231005222231093

Flake Normal Map

  • 上图中圆盘内的彩色小点是通过噪声函数flake(个人翻译为薄片) normal map生成的,该函数基于[Cellular Noise]https://www.cnblogs.com/chenglixue/p/17742395.html

  • 用途:汽车喷漆,及各种细小的闪光点材质

  • 参数

    • Flake Scale:缩放薄片大小
      image-20231005183450854
    • FlakeSize:薄片尺寸大小
      image-20231005202917200
    • Flake Size Variance:薄片尺寸的随机性——0所有薄片尺寸都相同
    • Flake Normal Orientation:blend 物体表面法线 和 随机薄片的法线
      image-20231005203908518
  • 实现

    float3 hash3(float3 p) 
    {
        p = float3(dot(p, float3(127.1, 311.7, 74.7)),
    			  dot(p, float3(269.5, 183.3, 246.1)),
    			  dot(p, float3(113.5, 271.9, 124.6)));
    
        return frac(sin(p) * 43758.5453123);
    }
    
    float4 flakesNormalMap(float2 fragCoord, float3 view, float flakeScale, float flakeSize, float flakeSizeVariance, float flakeNormalOrientation)
    {
        float safeFlakeSizeVariance = clamp(flakeSizeVariance, 0.1, 1.0);
        
        // 用于求得九个特征点
        const float3 cellCenters[9] = {
    		float3( 0.5,  0.5,  0.0),
    		float3( 1.5,  0.5,  0.0),
    		float3( 1.5,  1.5,  0.0),
    		float3( 0.5,  1.5,  0.0),
    		float3(-0.5,  1.5,  0.0),
    		float3(-0.5,  0.5,  0.0),
    		float3(-0.5, -0.5,  0.0),
    		float3( 0.5, -0.5,  0.0),
    		float3( 1.5, -0.5,  0.0)
    	};
    
        // 目标像素点
        float3 position = float3(fragCoord.x, fragCoord.y, 0.f);
        position = flakeScale * position;
    
        float3 base = floor(position);
    
        float3 nearestCell = float3(0.f, 0.f, 1.f);
    
        int nearestCellIndex = -1;
    
        // 计算目标点到九个特征点的距离
        for(int cellIndex = 0; cellIndex < 9; ++cellIndex)
        {
            float3 cellCenter = base + cellCenters[cellIndex];
    
            float3 centerOffset = hash3(cellCenter) * 2.f - 1.f;
            centerOffset[2] *= safeFlakeSizeVariance;
            centerOffset = normalize(centerOffset);
    
            cellCenter += 0.5 * centerOffset;
    
            float cellDistance = distance(position, cellCenter);
            //是否是最小距离
            if(cellDistance < flakeSize && cellCenter[2] < nearestCell[2])
            {
                nearestCell = cellCenter;
                nearestCellIndex = cellIndex;
            }
        }
    
        float4 result = float4(0.f, 0.f, 1.f, 0.f);
        if(nearestCellIndex != -1)
        {
            float3 randomNormal = hash3(base + cellCenters[nearestCellIndex] + float3(0.f, 0.f, 1.5f));
            randomNormal = 2.f * randomNormal - 1.f;
            randomNormal = faceforward(randomNormal, view, randomNormal);
            randomNormal = normalize(lerp(randomNormal, float3(0.f, 0.f, 1.f), flakeNormalOrientation));
    
            result.xyz = float3(randomNormal.x, randomNormal.y, randomNormal.z);
            result.w = 1.f;
        }   
    
        return result;
    }
    

    在UE中如下,”GlobalFun”custom node写有如上代码,而”Flakes Normal Map”custom node用于运行flakesNormalMap()函数
    image-20231005212607998

实现

前置设置

  • 由于亚克力是中透明材质,因此需要将”Blend Model”设为”TranslucentColoredTransmittance”
  • “Lighting Mode”设为”Surface ForwardShading”。测试了一下,改模式下材质的闪亮效果最佳,当然也是最耗性能的,而Surface TranslucencyVolume的效果非常差
  • “Refraction Method”设为”Index Of Refraction”
  • 根节点处的”IOR”赋值”1.22”
    image-20231005220940611

确定UV

  • 考虑到需要将得到的flake运用到3维物体上,需要确定uv坐标来自哪两个轴。如果这里仅仅简单地使用物体的世界坐标会得到如下的错误结果
    image-20231005213046353

    为了根据对应的面求得正确的flake结果,需要根据vertex normal的值来确定正确的面。大致思路如下
    image-20231005213224715

  • 不难得到如下效果
    image-20231005213431102

制作薄片

  • 目前只是求得法线信息,还需为这些薄片赋予normal、F0、F90、roughness、MFP
  • Normal制作如下
    image-20231005213835555
  • F0 F90 Roughness制作如下
    image-20231005215442304
  • SSS MFP
    image-20231005215531043
  • 效果如下
    image-20231005215555730

刻上图案

  • 为了在物体表面刻上图案,需要在水平层面上进行blend
  • 实现
    • 图案层
      image-20231005220055867
    • 水平blend
      image-20231005220107514
    • 效果如下
      image-20231005220130538

制作亚克力

  • 用了将亚克力涂于表面,需要在垂直层面进行blend
  • 实现
    • 亚克力
      image-20231005220331719
    • 垂直层面的blend
      image-20231005220440859
    • 效果
      image-20231005221115546

折射效果

  • 亚克力材质是带有较弱的折射效果的,这里使用POM实现。不使用IOR,是因为IOR只会改变材质后面物体的折射效果

  • 实现

    • POM
      image-20231005221249684
      上图中的“POM UV”为”路由节点”(不懂得可以看这篇)

    • 在normal map 和 blend处加上POM UV
      image-20231005221425645

      image-20231005221500826

    • 效果
      添加POM前
      image-20231005221805992

      添加POM后
      image-20231005221832492

后续

reference

https://docs.chaos.com/display/OSLShaders/Flakes+normal+map

https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/stdlib/genglsl/lib/mx_noise.glsl

https://help.autodesk.com/view/ARNOL/ENU/?guid=arnold_user_guide_ac_texture_shaders_ac_texture_flakes_html
https://zhuanlan.zhihu.com/p/658057646

UE5 内容实例