zoukankan      html  css  js  c++  java
  • 翻译10 Unity Shader GUI 拓展2-细节纹理

    把自身阴影烘焙进材质
    增加细节纹理部分
    支持更丰富的shader变体
    一次编辑多个材质球

    1 遮挡区域的Self-Shading

    美术能够创作非常复杂丰富的表面纹理,它只是一个视错觉。为了增强表面纹理视觉真实感,引入Self-Shading。如何增强呢?通常我们使用了法线来增强模型表面的凹凸层次感,法线带来的视觉增强是第一步,但是法线只适用于采样直接光照下。现在开始第二步增强,给凹凸表面引入阴影:凸向凹投射阴影

    1.1 Occlusion Map

    使用遮挡纹理增加self-shadings,也是灰度图,凹趋近黑色0。在Properties声明和拓展gui:

    [NoScaleOffset]_OcclusionMap("OcclusionMap", 2D) = "white"{}
    _Occlusion("Occlusion", Range(0,1)) = 0
    
    #pragma shader_feature _ _OCCLUSION_MAP

    注意shader_feature _ _OCCLUSION_MAPshader_feature _OCCLUSION_MAP是等效的。但是下面

    //这两个不等效
    #pragma shader_feature _SMOOTHNESS_ALBEDO _SMOOTHNESS_METALLIC
    #pragma shader_feature _ _SMOOTHNESS_ALBEDO _SMOOTHNESS_METALLIC

    总结一下shader_feature ,只有一个关键字默认生成<no keywords defined>,若有多个关键字第一个关键字会替代<no keywords defined>. 在使用手动收集ShaderVariantCollection要多加注意。而multi_compile有单个关键字时必须加_.

    1.2 Occlusion GUI_Extension

    void OcclusionShow()
    {
        EditorGUI.BeginChangeCheck();
        MaterialProperty mp = MakerMapWithScaleShow("_OcclusionMap", "_Occlusion", false, "遮挡纹理");
        if (EditorGUI.EndChangeCheck())
        {
            SetKeyword("_OCCLUSION_MAP", mp.textureValue);
        }
    }

    1.3 直接光阴影

    创建采样函数

    float GetOcclusion(Interpolators i) {
    #ifdef _OCCLUSION_MAP
    	return tex2D(_OcclusionMap, i.uv).g;
    #endif
    	return 1;
    }

    但是由于阴影的强度有可调需求,需要动态改变。结合_OcclusionStrength做线性插值

    float GetOcclusion(Interpolators i) {
    #ifdef _OCCLUSION_MAP
    	float g = tex2D(_OcclusionMap, i.uv).g
    	return lerp(1, g, _OcclusionStrength);
    #endif
    	return 1;
    }

    然后将采样得到的值作用于光照颜色内,包括直接光和间接光,这里是直接光

    UnityLight CreateLight(Interpolators i) {
    //。。。
    	UNITY_LIGHT_ATTENUATION(attenuation, i, i.worldPos);
    	attenuation *= GetOcclusion(i);
    	light.color = _LightColor0.rgb * attenuation;
    	light.ndotl = DotClamped(i.normal, light.dir);
    	return light;
    }

    image image

    图1.1 without and with occlusion map

    图1.1带有遮挡纹理的凹陷阴影过渡gif显示:

    directLight_occlusion

    1.4 间接光阴影

    直接光采样下凹陷越深阴影越重,但不是那么明显。这是因为除了直接光以外还有间接光,幸好OcclusionMap并不是针对特定光线的纹理,现在来给他增加间接光采样,然后看看效果如何。

    UnityIndirect CreateIndirectLight(Interpolators i, float3 viewDir) {
    #if defined(VERTEXLIGHT_ON)
        //...
        float occlusion = GetOcclusion(i);
        indirectLight.diffuse *= occlusion;
        indirectLight.specular *= occlusion;
    #endif
        return indirectLight;
    }

    image image

    图1.2 wihtout and with occlusion

    把图1.2与图1.1对比,可以明显感觉到Occlusion纹理好似专门针对间接光而制作,它随着凹陷越深阴影越明显,甚至有点过头了。那么我们何不把直接光采样这步去掉,看看它的效果如何。

    UnityLight CreateLight(Interpolators i) {
    //。。。
    	UNITY_LIGHT_ATTENUATION(attenuation, i, i.worldPos);
    	attenuation *= GetOcclusion(i);
    	light.color = _LightColor0.rgb * attenuation;
    	light.ndotl = DotClamped(i.normal, light.dir);
    	return light;
    }

    image image

    图1.3 without and with occlusion

    把图1.1、1.2、1.3对比,感觉图1.3的效果适中,看起来舒服。原文翻译:就Occlusion而言,它具有相当大真实感。虽然如此,我们通常会发现游戏里的遮挡图也被用在直接光上。Unity老旧的shader就是这样做的,虽然它不太真实,但是对灯光效果的控制提供了相当大的灵活性。

    SSAO:screen-space-ambient-occlusion。它是屏幕后处理效果,使用深度缓冲来动态创建整个帧的遮挡映射。它被用来增强屏幕的深度感,因为它是后处理效果,它在所有的灯光渲染之后被使用。这意味着那个阴影即使用了间接光也使用了直接光。因此它也是不真实的。

    1.5 合并纹理

    我们只用了遮挡纹理的G通道,而metallic金属纹理是存储在R通道,SmoothNess纹理存储在alpha通道。这意味着我们可以把三个纹理合并为一个纹理。

    image

    图1.4 合并后的纹理

    优势

    1. 单一纹理降低了内存和存储压力;

    弊端

    1. 这个Shader中它会采样两次;(可以手动优化为从同一纹理采样);
    2. 使用DXT5压缩后,纹理大小变小了但是它的质量也降低了。所幸这些纹理不要求太高的细节和精度。

    2 细节纹理

    增加细节纹理和法线,把细节纹理和法线设置为fade-out mipmap。

    image

    图2.1 细节纹理效果

    2.1 细节遮罩纹理

    根据图2.1效果,细节纹理覆盖整个表面后,看起来的效果不是太好。最好的效果是它不覆盖金属区域部分。所以,我们可以用细节遮罩纹理来控制这部分显示,这就好像蒙版测试。不同之处在于0表示没有细节,1表示完整的细节。

    Unity的Standard Shader使用了遮罩纹理的alpha通道,这张纹理四个通道存储了同样的值。

    2.2 Albedo Details

    调整Albedo纹理采样,必须基于detal mask纹理的采样值,在未修改和修改后的albedo之间插值。

    float3 GetAlbedo (Interpolators i) {
        float3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Tint.rgb;
        float3 details = tex2D(_DetailTex, i.uv.zw) * unity_ColorSpaceDouble;
        albedo = lerp(albedo, albedo * details, GetDetailMask(i));
        return albedo;
    }
    ...
    float4 MyFragmentProgram (Interpolators i) : SV_TARGET {
        …
    //    float3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Tint.rgb;
    //    albedo *= tex2D(_DetailTex, i.uv.zw) * unity_ColorSpaceDouble;
    
        float3 specularTint;
        float oneMinusReflectivity;
        float3 albedo = DiffuseAndSpecularFromMetallic(
            GetAlbedo(i), GetMetallic(i), specularTint, oneMinusReflectivity
        );
        …
    }

    2.3 Normal Details

    对于法线,同样需要相同的调整。但是,这里的细节不符合与未修改的切线空间法向量相对应。因为原作者给的这张法线纹理不是切线空间。需要手动匹配一次。

    void InitializeFragmentNormal(inout Interpolators i) {
        float3 mainNormal = UnpackScaleNormal(tex2D(_NormalMap, i.uv.xy), _BumpScale);
        float3 detailNormal = UnpackScaleNormal(tex2D(_DetailNormalMap, i.uv.zw), _DetailBumpScale);
        detailNormal = lerp(float3(0, 0, 1), detailNormal, GetDetailMask(i));
        float3 tangentSpaceNormal = BlendNormals(mainNormal, detailNormal);
    ...
    }

    image

    图2.2 Mask Details

  • 相关阅读:
    GIT在Linux上的安装和使用简介心得
    Android开发环境使用到工具的认识心得
    Android系统移植与驱动开发心得
    嵌入式Linux的调试技术
    硬件抽象层——HAL
    Linux代码的重用与强行卸载Linux驱动
    控制发光二极管
    详细讲解Linux驱动程序
    搭建测试环境——针对S3C6410开发板
    有了源代码,当然还需要编译喽!!
  • 原文地址:https://www.cnblogs.com/baolong-chen/p/12348697.html
Copyright © 2011-2022 走看看