卡通漫画网点波点渲染效果shader

        在这篇技术指导中,我们将介绍一种节省性能且简单的方式来实现卡通网点效果。然而,这种方法在模型表面特别平直时效果可能不佳。

实现原理

        如下图所示,我们使用本地坐标或世界坐标作为距离来计算一个点阵,并利用模型的表面与这些点阵相切,再将相切的结果绘制到模型表面上。具体步骤如下:

  1. 计算波点位置:通过模型空间坐标或世界空间坐标计算波点的位置。
  2. 确定波点范围:根据光照方向和表面法线,计算波点的范围。
  3. 应用颜色贴图:使用 ramp texture 对计算好的波点和光方向进行着色。
代码片段

代码片段

        我们这里使用的是模型空间坐标来计算波点。

                float vertexPoint = length(frac(i.positionOS.xyz * _DotSize) - (0.5).xxx );
  • i.positionOS 是顶点在模型空间中的位置。
  • _DotSize 控制波点的大小。
  • frac 函数返回输入值的小数部分,用于生成重复的网格图案。
  • length 函数计算从中心到当前顶点的小数部分距离,形成圆形波点。

        接下来,我们计算需要波点的范围:

float halfLambert = dot(_MainLightPosition.xyz,i.normalWS) * 0.5 + 0.5;
float externalScale = 1.0 - smoothstep(_ExternalOffset, _ExternalOffset + _ExternalSoft, halfLambert);
float centerScale = smoothstep(_CenterOffset, _CenterOffset + _CenterSoft, halfLambert);
float pointPower = externalScale * centerScale;
float pointMask = round(pow(vertexPoint, pointPower));
  • halfLambert 是基于光照方向和表面法线计算出的半 Lambert 光照值,用于模拟光照强度。
  • _MainLightPosition 是主光源的位置。
  • smoothstep 函数用于平滑过渡,使得外部和中心区域有柔和的渐变效果。
  • externalScale 和 centerScale 控制波点外部和中心区域的范围及过渡。
  • pointPower 是外部和中心范围的乘积,用于调节波点强度。
  • pointMask 使用 round(pow(vertexPoint, pointPower)) 来生成最终的波点遮罩。

        

        最后,使用 ramp texture 对计算好的波点和光方向进行着色:

float2 colorUV = lerp( float2(_PointColorRange, 0.1), float2(halfLambert,0.1 ),  pointMask);
float3 finalColor = tex2D(_RampTex, colorUV);
  • lerp 函数用于插值,根据 pointMask 在两个颜色之间进行线性插值。
  • _PointColorRange 控制波点颜色调节范围。
  • tex2D 函数从贴图中采样颜色。

完整代码(HLSL)

Shader "Unlit/PointArt_Code"
{
    Properties
    {
        [SingleLineTexture]_RampTex ("颜色贴图", 2d) = "white" {}
        _PointColorRange ("波点颜色调节", range(0.0, 1.0)) = 0.1
        
        _DotSize ("波点大小" , float) = 1
        
        _ExternalOffset("波点外部范围调整", Range(0.0, 1.0)) = 0.3
        _ExternalSoft("波点外部范围过度", Range(0.0, 1.0)) = 0.2
        
        _CenterOffset ("波点中间范围调整", Range(0.0, 1.0)) = 0.1   
        _CenterSoft ("波点中间范围过度", Range(0.0, 1.0)) = 0.1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        
        Pass
        {
            Name "Forward"
            Tags {"LightMode" = "UniversalForwardOnly" }
            
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"


            struct appdata
            {
                float4 positionOS : POSITION;
                float3 normalOS : NORMAL;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float4 positionOS : TEXCOORD0 ;
                float3 normalWS : TEXCOORD1 ;
                
            };

            CBUFFER_START(UnityPerMaterial)
            float _DotSize;

            float _ExternalOffset;
            float _ExternalSoft;

            float _CenterOffset;
            float _CenterSoft;

            float _PointColorRange;
            CBUFFER_END

            sampler2D _RampTex;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = TransformObjectToHClip(v.positionOS);
                o.positionOS = v.positionOS;
                
                o.normalWS = TransformObjectToWorldNormal(v.normalOS) ; 
                return o;
            }

            float4 frag (v2f i) : SV_Target
            {
                //使用模型空间坐标计算波点,根据需要是可以使用世界空间的坐标。
                float vertexPoint = length(frac(i.positionOS.xyz * _DotSize) - (0.5).xxx );
                //计算圆点的范围
                float halfLambert = dot(_MainLightPosition.xyz,i.normalWS) * 0.5 + 0.5;
                float externalScale = 1.0 - smoothstep(_ExternalOffset, _ExternalOffset + _ExternalSoft, halfLambert);
                float centerScale = smoothstep(_CenterOffset, _CenterOffset + _CenterSoft, halfLambert);
                float pointPower = externalScale * centerScale;
                float pointMask = round(pow(vertexPoint, pointPower));
                //采样贴图颜色
                float2 colorUV = lerp( float2(_PointColorRange, 0.1), float2(halfLambert,0.1 ),  pointMask);
                float3 finalColor = tex2D(_RampTex, colorUV);
                
                return float4(finalColor,1);
            }
            ENDHLSL
        }
    }
    CustomEditor "UnityEditor.ShaderGraphUnlitGUI"
    FallBack "Hidden/Shader Graph/FallbackError"

}

总结

以上代码展示了如何通过简单且高效的方法实现卡通网点效果。这种方法利用了模型空间或世界空间的坐标,通过对光照方向和表面法线的计算,将网点效果映射到模型表面。尽管在模型特别平直时效果可能不佳,但总体上是一种性能友好的实现方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值