微信扫一扫 分享朋友圈

已有 2788 人浏览分享

开启左侧

基于Mactcap实现车漆效果

[复制链接]
2788 1

最近研究了一下车漆的shader,然后在互联网的浪潮中,发现了毛星云大佬之前的写的一个基于matcap的车漆shader。然后我就在大佬的基础之上又增加了一部分。

原文链接:【Unity Shader编程】之十六 基于MatCap实现适于移动平台的“次时代”车漆Shader_mb61c46a7ab1eee的技术博客_51CTO博客

首先讲一下整个shader的实现思路。通过matcap代替光照计算,直接获得“光照信息”。再基于漫反射和立方体贴图来实现车漆的效果。

我试了一下毛星云大佬的Shader,效果如下图

image.png

效果看着其实不错,但是总感觉差点什么。我寻觅了半天以后,感觉现实车漆其实高光会多一点,像下图,车的边缘都会有一些高光存在。这个我觉得是因为我们在观察车漆的时候,光线都是自然光,都是从头顶打光下来,并且通过清漆的折射,从而导致车身边缘都会有一部分高光。但是我们看上图就会发现,边缘其实是有点发黑的。不过这个效果实现起来就比较容易,直接用菲涅尔边缘光就可以完成了。

image.png

//菲涅尔边缘光 half3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world); half NdotV = saturate(dot(normal_world, viewDir)); half fresnel = pow((1.0 - NdotV), 5.0f) _FresnelIntensity 5;

那么加入了边缘光后,我们就可以对比一下效果了

image.png

(右边是增加了菲涅尔边缘光效果)

所有的代码如下:

Pass
{
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag

    #include "UnityCG.cginc"

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

    struct v2f
    {
        float2 uv : TEXCOORD0;
        float3 normal_viewspace : TEXCOORD1;          
        float3 pos_world : TEXCOORD2;
        float3 viewDir : TEXCOORD3;
        float3 worldSpaceIncident : TEXCOORD4;
        float3 normal_world : TEXCOORD5;
        float4 vertex : SV_POSITION;
    };

    float4 _MainColor;
    float4 _DetailColor;
    sampler2D _DetailMap;
    float4 _DetailMap_ST;
    float _DetailMapDepthOffset;
    float4 _DiffuseColor;
    sampler2D _DiffuseMap;
    float _DiffuseIntensity;
    float4 _DiffuseMap_ST;
    sampler2D _Matcap;
    float _MatcapIntensity;
    float4 _ReflectionColor;
    samplerCUBE _ReflectionMap;
    float _ReflectionIntensity;
    float _FresnelIntensity;

    v2f vert (appdata v)
    {
        v2f o;
        o.vertex = UnityObjectToClipPos(v.vertex);
        o.uv = TRANSFORM_TEX(v.uv, _DiffuseMap);
        o.uv = TRANSFORM_TEX(v.uv, _DetailMap);
        //将法线从模型空间转换到世界空间
        float3 normal_world = mul(float4(v.normal, 0.0), unity_WorldToObject);
        o.normal_world = normal_world;
        //将法线从世界空间转换到相机空间
        float3 normal_viewspace = mul(UNITY_MATRIX_V, float4(normal_world, 0.0)).xyz;
        o.normal_viewspace = normal_viewspace;
        //世界空间位置
        float3 pos_world = mul(unity_ObjectToWorld, v.vertex);
        o.pos_world = pos_world;
        //世界空间反射向量
        float3 worldSpaceIncident = reflect((pos_world - _WorldSpaceCameraPos), normal_world);
        o.worldSpaceIncident = worldSpaceIncident;
        return o;
    }

    fixed4 frag(v2f i) : SV_Target
    {
        //镜面反射颜色
        float3 worldSpaceReflection = normalize(i.worldSpaceIncident);
        float3 reflectionColor = texCUBE(_ReflectionMap, worldSpaceReflection).rgb * _ReflectionColor.rgb * _ReflectionIntensity;

        //matcap
        half3 normal_world = normalize(i.normal_world);
        half3 normal_viewspace = normalize(i.normal_viewspace);
        half2 uv_matcap = (normal_viewspace.xy + float2(1.0, 1.0)) * 0.5;
        half4 matcap_color = tex2D(_Matcap, uv_matcap) * _MatcapIntensity;
        half4 diffuse_color = tex2D(_DiffuseMap, i.uv) * _DiffuseColor * _DiffuseIntensity;

        //菲涅尔
        half3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world);
        half NdotV = saturate(dot(normal_world, viewDir));
        half fresnel = pow((1.0 - NdotV), 5.0f) * _FresnelIntensity * 5;

        half4 matcap_fresnel = matcap_color + fresnel;

        //细节颜色
        half4 Detail_Color = tex2D(_DetailMap, i.uv);
        Detail_Color = Detail_Color * _DetailColor;

        //车漆颜色
        //half3 main_color = lerp(lerp(_MainColor, diffuse_color.rgb, diffuse_color.a), reflectionColor, _ReflectionIntensity);
        //half3 main_reflect_color = main_color * reflectionColor;
        //half4 main_color = matcap_fresnel * diffuse_color + half4(reflectionColor, 1.0) * _MainColor + Detail_Color;
        //float3 final_Color = lerp(diffuse_color, matcap_color.rgb, fresnel) * _MainColor.rgb * _DiffuseColor + reflectionColor;
        half3 final_color = lerp( matcap_color, reflectionColor, fresnel) * _MainColor.rgb + diffuse_color;
        return half4(final_color, 1.0);
    }
    ENDCG
}

} 看着代码来讲一下关于Matcap的实现方法,

Matcap主要的效果就是,将Matcap贴图使用在模型上以后,模型所展现的贴图始终是基于相机空间不变的。(讲的好像有点抽象),下面这个是使用的Matcap贴图。

image.png

我们将这张图贴到一个球体上以后,我们可以看到两个高光的方块始终是在屏幕中不动的

817e549026ae6909ea69cc699bf888b8.gif

所以首先要做的就是将法线从模型空间转到相机空间。我用了两步,先从模型空间转到世界空间,再从世界空间转到相机空间。因为在后面的计算中,还要用到世界空间下的法线数据,所以就通过两步去做了。在这里有个点要强调一下就是,Matcap之所以要转换的是法线数据而不是顶点数据,是因为MatCap材质是基于表面法线方向的反射颜色来渲染的,Matcap材质不受光照影响,只能提供相对于表面法线方向的反射颜色。

我们将得到的相机空间下的法线数据加1除以2,是为了保证法线数据能够在0到1内。然后我们采样Matcap贴图即可。

后面的就很简单了,采样了Diffuse贴图和镜面反射贴图。通过光照探针来烘焙出一张立方体贴图即可。后面还采样了一个细节贴图,这个是想加一些车漆的橘皮效果,但是我手上没找到好的橘皮的贴图资源,所以就在Shader里面写了但是没有加。最后在计算最终颜色的时候,可以看到我算了好几次,都没找到一个合适的计算方法......最后也是Chat了一下,然后浅浅修改了一下就用了。(得去学一下这种最后颜色的计算方法去)image.png

最后的效果其实还可以,但是我感觉还是差点。我继续研究一下,舍弃Matcap,直接采用光照计算,用法线贴图把橘皮效果做出来以后,试一下效果怎么样。如果还不行就写一套PBR了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

评分

参与人数 1创新分 +40 技术分 +15 艺术分 +15 收起 理由
admin + 40 + 15 + 15

查看全部评分

评论 1

蓝橙熊  咒语学徒  发表于 2023-5-31 15:20:00 | 显示全部楼层
学到了
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

0

关注

0

粉丝

2

主题
精彩推荐
热门资讯
网友晒图
图文推荐
  • iOS App

  • 安卓App

Archiver|手机版|小黑屋|技你太美101

GMT+8, 2024-11-24 10:16 , Processed in 0.096041 second(s), 34 queries .

Powered by 技你太美101

© 2024 JNTM101 Team