zoukankan      html  css  js  c++  java
  • Occlusion 和 Detail Map

    1. Occlusion  环境光遮蔽图,在3D游戏经常会听到一个词环境光遮蔽(Ambient Occlusion)。

    百度百科的说明 大概意思了解下就好。

    AO是来描绘物体和物体相交或靠近的时候遮挡周围漫反射光线的效果,可以解决或改善漏光、飘和阴影不实等问题,解决或改善场景中缝隙、褶皱与墙角、角线以及细小物体等的表现不清晰问题,综合改善细节尤其是暗部阴影,增强空间的层次感、真实感,同时加强和改善画面明暗对比,增强画面的艺术性

    上面的斯坦福龙呈现在一个均匀照明的环境中。 模型中有一些较黑和较亮的区域,但照明大部分都是均匀的。 尽管有相当复杂的几何形状,但龙看上去很平,没有明显的深度感觉。

    下面是启用了环境光遮蔽的相同模型。

    环境光遮蔽 从字面上就是对于环境光影响。对于直接光照不影响。

    Unity里面实现下

     1 half Occlusion(float2 uv)
     2 {
     3 #if (SHADER_TARGET < 30)
     4     // SM20: instruction count limitation
     5     // SM20: simpler occlusion
     6     return tex2D(_OcclusionMap, uv).g;
     7 #else
     8     half occ = tex2D(_OcclusionMap, uv).g;
     9     return LerpOneTo (occ, _OcclusionStrength);
    10 #endif
    11 }
    inline UnityGI UnityGI_Base(UnityGIInput data, half occlusion, half3 normalWorld)
    {
        UnityGI o_gi;
        ResetUnityGI(o_gi);
    
        // Base pass with Lightmap support is responsible for handling ShadowMask / blending here for performance reason
        #if defined(HANDLE_SHADOWS_BLENDING_IN_GI)
            half bakedAtten = UnitySampleBakedOcclusion(data.lightmapUV.xy, data.worldPos);
            float zDist = dot(_WorldSpaceCameraPos - data.worldPos, UNITY_MATRIX_V[2].xyz);
            float fadeDist = UnityComputeShadowFadeDistance(data.worldPos, zDist);
            data.atten = UnityMixRealtimeAndBakedShadows(data.atten, bakedAtten, UnityComputeShadowFade(fadeDist));
        #endif
    
        o_gi.light = data.light;
        o_gi.light.color *= data.atten;
    
        #if UNITY_SHOULD_SAMPLE_SH
            o_gi.indirect.diffuse = ShadeSHPerPixel(normalWorld, data.ambient, data.worldPos);
        #endif
    
        #if defined(LIGHTMAP_ON)
            // Baked lightmaps
            half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, data.lightmapUV.xy);
            half3 bakedColor = DecodeLightmap(bakedColorTex);
    
            #ifdef DIRLIGHTMAP_COMBINED
                fixed4 bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER (unity_LightmapInd, unity_Lightmap, data.lightmapUV.xy);
                o_gi.indirect.diffuse += DecodeDirectionalLightmap (bakedColor, bakedDirTex, normalWorld);
    
                #if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN)
                    ResetUnityLight(o_gi.light);
                    o_gi.indirect.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap (o_gi.indirect.diffuse, data.atten, bakedColorTex, normalWorld);
                #endif
    
            #else // not directional lightmap
                o_gi.indirect.diffuse += bakedColor;
    
                #if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN)
                    ResetUnityLight(o_gi.light);
                    o_gi.indirect.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap(o_gi.indirect.diffuse, data.atten, bakedColorTex, normalWorld);
                #endif
    
            #endif
        #endif
    
        #ifdef DYNAMICLIGHTMAP_ON
            // Dynamic lightmaps
            fixed4 realtimeColorTex = UNITY_SAMPLE_TEX2D(unity_DynamicLightmap, data.lightmapUV.zw);
            half3 realtimeColor = DecodeRealtimeLightmap (realtimeColorTex);
    
            #ifdef DIRLIGHTMAP_COMBINED
                half4 realtimeDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_DynamicDirectionality, unity_DynamicLightmap, data.lightmapUV.zw);
                o_gi.indirect.diffuse += DecodeDirectionalLightmap (realtimeColor, realtimeDirTex, normalWorld);
            #else
                o_gi.indirect.diffuse += realtimeColor;
            #endif
        #endif
    
        o_gi.indirect.diffuse *= occlusion;
        return o_gi;
    }

    使用的地方影响的diffuse 环境光的强度。

    Detail Map Unity提供两个,一个DetailAlbedo 一个是DetailNormal

    DetailAlbodo

    float4 TexCoords(VertexInput v)
    {
        float4 texcoord;
        texcoord.xy = TRANSFORM_TEX(v.uv0, _MainTex); // Always source from uv0
        texcoord.zw = TRANSFORM_TEX(((_UVSec == 0) ? v.uv0 : v.uv1), _DetailAlbedoMap);
        return texcoord;
    }
    
    VertexOutputForwardBase vertForwardBase (VertexInput v)
    {
        UNITY_SETUP_INSTANCE_ID(v);
        VertexOutputForwardBase o;
        UNITY_INITIALIZE_OUTPUT(VertexOutputForwardBase, o);
        UNITY_TRANSFER_INSTANCE_ID(v, o);
        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    
        float4 posWorld = mul(unity_ObjectToWorld, v.vertex);
        #if UNITY_REQUIRE_FRAG_WORLDPOS
            #if UNITY_PACK_WORLDPOS_WITH_TANGENT
                o.tangentToWorldAndPackedData[0].w = posWorld.x;
                o.tangentToWorldAndPackedData[1].w = posWorld.y;
                o.tangentToWorldAndPackedData[2].w = posWorld.z;
            #else
                o.posWorld = posWorld.xyz;
            #endif
        #endif
        o.pos = UnityObjectToClipPos(v.vertex);
    
        o.tex = TexCoords(v);
        o.eyeVec = NormalizePerVertexNormal(posWorld.xyz - _WorldSpaceCameraPos);
        float3 normalWorld = UnityObjectToWorldNormal(v.normal);
        #ifdef _TANGENT_TO_WORLD
            float4 tangentWorld = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);
    
            float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWorld, tangentWorld.xyz, tangentWorld.w);
            o.tangentToWorldAndPackedData[0].xyz = tangentToWorld[0];
            o.tangentToWorldAndPackedData[1].xyz = tangentToWorld[1];
            o.tangentToWorldAndPackedData[2].xyz = tangentToWorld[2];
        #else
            o.tangentToWorldAndPackedData[0].xyz = 0;
            o.tangentToWorldAndPackedData[1].xyz = 0;
            o.tangentToWorldAndPackedData[2].xyz = normalWorld;
        #endif
    
        //We need this for shadow receving
        UNITY_TRANSFER_SHADOW(o, v.uv1);
    
        o.ambientOrLightmapUV = VertexGIForward(v, posWorld, normalWorld);
    
        #ifdef _PARALLAXMAP
            TANGENT_SPACE_ROTATION;
            half3 viewDirForParallax = mul (rotation, ObjSpaceViewDir(v.vertex));
            o.tangentToWorldAndPackedData[0].w = viewDirForParallax.x;
            o.tangentToWorldAndPackedData[1].w = viewDirForParallax.y;
            o.tangentToWorldAndPackedData[2].w = viewDirForParallax.z;
        #endif
    
        UNITY_TRANSFER_FOG(o,o.pos);
        return o;
    }

    在顶点着色器的 先填充UV

    o.tex = TexCoords(v);

    像素着色器

    inline FragmentCommonData RoughnessSetup(float4 i_tex)
    {
        half2 metallicGloss = MetallicRough(i_tex.xy);
        half metallic = metallicGloss.x;
        half smoothness = metallicGloss.y; // this is 1 minus the square root of real roughness m.
    
        half oneMinusReflectivity;
        half3 specColor;
        half3 diffColor = DiffuseAndSpecularFromMetallic(Albedo(i_tex), metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity);
    
        FragmentCommonData o = (FragmentCommonData)0;
        o.diffColor = diffColor;
        o.specColor = specColor;
        o.oneMinusReflectivity = oneMinusReflectivity;
        o.smoothness = smoothness;
        return o;
    }

    采样颜色

    inline half3 DiffuseAndSpecularFromMetallic (half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity)
    {
        specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);
        oneMinusReflectivity = OneMinusReflectivityFromMetallic(metallic);
        return albedo * oneMinusReflectivity;
    }
    half3 Albedo(float4 texcoords)
    {
        half3 albedo = _Color.rgb * tex2D (_MainTex, texcoords.xy).rgb;
    #if _DETAIL
        #if (SHADER_TARGET < 30)
            // SM20: instruction count limitation
            // SM20: no detail mask
            half mask = 1;
        #else
            half mask = DetailMask(texcoords.xy);
        #endif
        half3 detailAlbedo = tex2D (_DetailAlbedoMap, texcoords.zw).rgb;
        #if _DETAIL_MULX2
            albedo *= LerpWhiteTo (detailAlbedo * unity_ColorSpaceDouble.rgb, mask);
        #elif _DETAIL_MUL
            albedo *= LerpWhiteTo (detailAlbedo, mask);
        #elif _DETAIL_ADD
            albedo += detailAlbedo * mask;
        #elif _DETAIL_LERP
            albedo = lerp (albedo, detailAlbedo, mask);
        #endif
    #endif
        return albedo;
    }

    这里detailAlbedo有多种的混合模式。可以选择。

    DetailNormal

    // parallax transformed texcoord is used to sample occlusion
    inline FragmentCommonData FragmentSetup (inout float4 i_tex, float3 i_eyeVec, half3 i_viewDirForParallax, float4 tangentToWorld[3], float3 i_posWorld)
    {
        i_tex = Parallax(i_tex, i_viewDirForParallax);
    
        half alpha = Alpha(i_tex.xy);
        #if defined(_ALPHATEST_ON)
            clip (alpha - _Cutoff);
        #endif
    
        FragmentCommonData o = UNITY_SETUP_BRDF_INPUT (i_tex);
        o.normalWorld = PerPixelWorldNormal(i_tex, tangentToWorld);
        o.eyeVec = NormalizePerPixelNormal(i_eyeVec);
        o.posWorld = i_posWorld;
    
        // NOTE: shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
        o.diffColor = PreMultiplyAlpha (o.diffColor, alpha, o.oneMinusReflectivity, /*out*/ o.alpha);
        return o;
    }
     o.normalWorld = PerPixelWorldNormal(i_tex, tangentToWorld);

    在像素着色器中,计算顶点法线

    float3 PerPixelWorldNormal(float4 i_tex, float4 tangentToWorld[3])
    {
    #ifdef _NORMALMAP
        half3 tangent = tangentToWorld[0].xyz;
        half3 binormal = tangentToWorld[1].xyz;
        half3 normal = tangentToWorld[2].xyz;
    
        #if UNITY_TANGENT_ORTHONORMALIZE
            normal = NormalizePerPixelNormal(normal);
    
            // ortho-normalize Tangent
            tangent = normalize (tangent - normal * dot(tangent, normal));
    
            // recalculate Binormal
            half3 newB = cross(normal, tangent);
            binormal = newB * sign (dot (newB, binormal));
        #endif
    
        half3 normalTangent = NormalInTangentSpace(i_tex);
        float3 normalWorld = NormalizePerPixelNormal(tangent * normalTangent.x + binormal * normalTangent.y + normal * normalTangent.z); // @TODO: see if we can squeeze this normalize on SM2.0 as well
    #else
        float3 normalWorld = normalize(tangentToWorld[2].xyz);
    #endif
        return normalWorld;
    }
    UNITY_TANGENT_ORTHONORMALIZE

    这里正交归一化一般不开启,就是重新把法线 副法线,切线正交。但是一般的精度就够了,不需要在顶点着色器中,重新做。

    half3 normalTangent = NormalInTangentSpace(i_tex);

    采样出切线空间中,法线的方向。

    #ifdef _NORMALMAP
    half3 NormalInTangentSpace(float4 texcoords)
    {
        half3 normalTangent = UnpackScaleNormal(tex2D (_BumpMap, texcoords.xy), _BumpScale);
    
    #if _DETAIL && defined(UNITY_ENABLE_DETAIL_NORMALMAP)
        half mask = DetailMask(texcoords.xy);
        half3 detailNormalTangent = UnpackScaleNormal(tex2D (_DetailNormalMap, texcoords.zw), _DetailNormalMapScale);
        #if _DETAIL_LERP
            normalTangent = lerp(
                normalTangent,
                detailNormalTangent,
                mask);
        #else
            normalTangent = lerp(
                normalTangent,
                BlendNormals(normalTangent, detailNormalTangent),
                mask);
        #endif
    #endif
    
        return normalTangent;
    }
    #endif

    这样就全部搞定。

    这里有个非常重要的一点。为什么早期渲染,金属感弱的问题。

    #define unity_ColorSpaceDielectricSpec half4(0.04, 0.04, 0.04, 1.0 - 0.04) // standard dielectric reflectivity coef at incident angle (= 4%)
    
     specColor = lerp (unity_ColorSpaceDielectricSpec.rgb, albedo, metallic);

    根据材料的金属度不一样,高光颜色是不一样的。根据物质的导电性特性区分。可以根据龚大的这篇文章

    金属,塑料,傻傻分不清楚

    // parallax transformed texcoord is used to sample occlusion
    inline FragmentCommonData FragmentSetup (inout float4 i_tex, float3 i_eyeVec, half3 i_viewDirForParallax, float4 tangentToWorld[3], float3 i_posWorld)
    {
        i_tex = Parallax(i_tex, i_viewDirForParallax);
    
        half alpha = Alpha(i_tex.xy);
        #if defined(_ALPHATEST_ON)
            clip (alpha - _Cutoff);
        #endif
    
        FragmentCommonData o = UNITY_SETUP_BRDF_INPUT (i_tex);
        o.normalWorld = PerPixelWorldNormal(i_tex, tangentToWorld);
        o.eyeVec = NormalizePerPixelNormal(i_eyeVec);
        o.posWorld = i_posWorld;
    
        // NOTE: shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
        o.diffColor = PreMultiplyAlpha (o.diffColor, alpha, o.oneMinusReflectivity, /*out*/ o.alpha);
        return o;
    }

    代码流程有点长,在顶点着色器的入口函数

    half4 fragForwardBaseInternal (VertexOutputForwardBase i)
    {
        UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy);
    
        FRAGMENT_SETUP(s)
    
        UNITY_SETUP_INSTANCE_ID(i);
        UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
    
        UnityLight mainLight = MainLight ();
        UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld);
    
        half occlusion = Occlusion(i.tex.xy);
        UnityGI gi = FragmentGI (s, occlusion, i.ambientOrLightmapUV, atten, mainLight);
    
        half4 c = UNITY_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect);
        c.rgb += Emission(i.tex.xy);
    
        UNITY_APPLY_FOG(i.fogCoord, c.rgb);
        return OutputForward (c, s.alpha);
    }

    通过宏,获取基本的diff颜色和高光,法线一些基础信息

    #define FRAGMENT_SETUP(x) FragmentCommonData x = 
        FragmentSetup(i.tex, i.eyeVec, IN_VIEWDIR4PARALLAX(i), i.tangentToWorldAndPackedData, IN_WORLDPOS(i));
    inline FragmentCommonData FragmentSetup (inout float4 i_tex, float3 i_eyeVec, half3 i_viewDirForParallax, float4 tangentToWorld[3], float3 i_posWorld)
    {
        i_tex = Parallax(i_tex, i_viewDirForParallax);
    
        half alpha = Alpha(i_tex.xy);
        #if defined(_ALPHATEST_ON)
            clip (alpha - _Cutoff);
        #endif
    
        FragmentCommonData o = UNITY_SETUP_BRDF_INPUT (i_tex);
        o.normalWorld = PerPixelWorldNormal(i_tex, tangentToWorld);
        o.eyeVec = NormalizePerPixelNormal(i_eyeVec);
        o.posWorld = i_posWorld;
    
        // NOTE: shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
        o.diffColor = PreMultiplyAlpha (o.diffColor, alpha, o.oneMinusReflectivity, /*out*/ o.alpha);
        return o;
    }
  • 相关阅读:
    Codeforces 177G2 Fibonacci Strings KMP 矩阵
    Codeforces Gym100187C Very Spacious Office 贪心 堆
    Codeforces 980F Cactus to Tree 仙人掌 Tarjan 树形dp 单调队列
    AtCoder SoundHound Inc. Programming Contest 2018 E + Graph (soundhound2018_summer_qual_e)
    BZOJ3622 已经没有什么好害怕的了 动态规划 容斥原理 组合数学
    NOIP2016提高组Day1T2 天天爱跑步 树链剖分 LCA 倍增 差分
    Codeforces 555C Case of Chocolate 其他
    NOIP2017提高组Day2T3 列队 洛谷P3960 线段树
    NOIP2017提高组Day2T2 宝藏 洛谷P3959 状压dp
    NOIP2017提高组Day1T3 逛公园 洛谷P3953 Tarjan 强连通缩点 SPFA 动态规划 最短路 拓扑序
  • 原文地址:https://www.cnblogs.com/wbaoqing/p/9244365.html
Copyright © 2011-2022 走看看