【Unity云消散】理论基础:实现边缘光

写在前面

既然想要实现云的消散效果,那么边缘光如何计算也是一个重点。

在Unity Shader入门精要的14章,介绍轮廓线渲染就介绍了——轮廓边检测,而边缘光也是需要先检测出轮廓边再进行的。

Unity3D Shader系列之边缘光RimLight

这篇博客给了我很大的启发,引言部分说了为什么花了很长时间把渲染管线、坐标变换这些基础的东西学清楚了,想要实现某种效果的时候还是要重新学习——欠缺算法知识。感觉还是要去主动想实现某种东西再有针对性的学习,这样学习得才更加深刻。

...有些扯远了,这篇文章简简单单实现边缘光,为之后实现云消散做铺垫,当然,卡通渲染边缘光还有很多讲究,包括描边也有很多需要学习的,之后再涉及。

1 简单的算法介绍

判断边缘

边缘光跟轮廓线渲染异曲同工,其核心都是找到边缘,而找边缘的算法原理很简单:当视线看向物体边缘的时候,视线与边缘的顶点法线是垂直的(很好理解吧!),那么我们依靠视线方向和模型面片法线的点积就能实现边缘判断,所以一切的核心最终凝炼成了:

// 点积
dot(worldNormal, worldViewDir);

判断是边缘后,叠一层边缘光颜色就行。

2 shader中实现

写过一点点shader的或许都有这样一种感觉:有时候明明道理很简单,但在frag里实现时又需要添加一些范围限定的函数(pow/saturate那些),才能得到最终的效果,这里也不例外:

// 1.点积
dot(worldNormal, worldViewDir);
// 2.(1 - 点积结果)&&(加个saturate控制到0,1之间) 
saturate(1 - dot(worldNormal, worldViewDir));

叠加必要的_RimColor和_RimIntensity,这样的话在shader中就是:

fixed4 rimLight = _RimColor * saturate(1 - dot(worldNormal, worldViewDir));

Shader中代码:

Shader "Unlit/rim"
{
    Properties
    {
        _BaseColor ("Base Color", Color) = (0,0,0,0)
        _RimColor ("Rim Color", Color) = (0,0,0,0)
        _RimPower ("Rim Power", Range(0.0001, 5.0)) = 1.0
        _RimIntensity ("Rim Intensity", Range(0, 100.0)) = 1.0

    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 worldNormal : TEXCOORD1;
                float3 worldViewDir : TEXCOORD2;
            };

            fixed4 _BaseColor;

            fixed4 _RimColor;
            float _RimIntensity;

            float _RimPower;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldViewDir = WorldSpaceViewDir(v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed3 worldNormal = normalize(i.worldNormal);
                fixed3 worldViewDir = normalize(i.worldViewDir);

                fixed4 diffuse = _BaseColor;

                fixed4 rimLight = _RimColor * saturate(1 - dot(worldNormal, worldViewDir)) * _RimIntensity;
                //fixed4 rimLight = _RimColor * pow(saturate(1 - dot(worldNormal, worldViewDir)), 1.0 / _RimPower) * _RimIntensity;

                fixed3 col = diffuse.rgb + rimLight.rgb;
                return fixed4(col,1.0);
            }
            ENDCG
        }
    }
}

效果:

衰减效果

原本的效果其实是没有什么衰减而言的,但是边缘光其实应该有个过渡效果,用上pow()就能实现啦。

算上pow:

fixed4 rimLight = _RimColor * pow(saturate(1 - dot(worldNormal, worldViewDir)), 1.0 / _RimPower) * _RimIntensity;

效果: 

【Cel-Shading】边缘光的实现 | Invictus maneo (x-wflo.github.io) 中有说遇到的问题

猜你喜欢

转载自blog.csdn.net/qq_41835314/article/details/128607818