问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

游戏开发中的遮挡半透明效果实现

创作时间:
作者:
@小白创作中心

游戏开发中的遮挡半透明效果实现

引用
CSDN
1.
https://m.blog.csdn.net/go_accepted/article/details/144951073

在游戏开发中,遮挡半透明效果是一种特殊的视觉效果,它能让被遮挡的物体部分呈现出半透明状态,从而增强游戏场景的真实感和沉浸感。本文将详细介绍遮挡半透明效果的原理、实现方法,并提供具体的Shader代码示例。

1、遮挡半透明效果是什么

在游戏开发中,遮挡半透明效果就是物体被挡住的部分,也能呈现出一种半透明效果而被看到(具体效果可以自定义)。比如当角色在建筑物之间穿行时,被遮挡部分能够呈现出半透明效果而被我们看到。遮挡半透明效果包含了两种显示效果,即挡住和没被挡住的部分,没被遮挡的部分正常显示,被遮挡的部分自定义显示(半透明、X光等等)。

2、遮挡半透明效果的基本原理

一句话描述它的基本原理:两个Pass渲染对象,一个Pass用于渲染被遮挡部分,一个Pass用于渲染未遮挡部分。被遮挡部分通过修改深度测试规则和关闭深度写入达到目的。

关键点:

  • 如何渲染遮挡部分
  • 如何渲染未遮挡部分

首先进行一个关于深度测试和深度写入的知识回顾,我们可以设置Pass的深度测试规则,只有通过了深度测试,该Pass才会执行进行渲染。不设置的话,默认为LEqual(小于等于),通过了深度测试后,我们可以使用 Zwrite On/Off 来决定是否将通过深度测试的值写入缓冲区。

  • 如何渲染遮挡部分

在第一个Pass中,我们按照想要的遮挡效果去实现Shader即可(比如半透明或X射线效果)。最关键的点,是需要修改该Pass的深度测试规则,并且关闭深度写入!

修改深度测试的目的:
只有自己的深度值大于深度缓冲中的值才渲染,相当于只有前方有遮挡时才渲染

关闭深度写入的目的:
如果前方有遮挡,又写入较大的深度值,执行第二个Pass时会通过深度写入(因为第二个Pass 默认是 LEqual),导致第二个Pass的内容通过深度测试被全部渲染,呈现出未被遮挡的错误效果!

  • 如何渲染未遮挡部分

在第二个Pass中我们只需要正常按需求(是否受光照影响等)渲染模型即可,不需要做什么特别处理

3、自定义半透明效果

普通的半透明效果非常容易实现,只需要设置混合因子即可。而要实现类似X射线的效果,也就是边缘不透明,中间透明的效果,可以利用菲涅尔反射的公式来得到:

其中:

  • R(θ) 表示入射角为θ时的反射率
  • R0 是垂直入射某介质时的反射率
  • V 是视角方向单位向量(入射角)
  • N 是顶点法线单位向量

将得到的 入射角为θ的反射率 作为自定义颜色的A值,便可以得到类似的结果

4、实现

基础实现

Shader "ShaderProj/16/OcclusionTransparent"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Alpha ("Alpha", Range(0, 1)) = 0.5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        Pass
        {
            ZTest Greater
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _Alpha;
            v2f vert (appdata_base v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                return fixed4(col.rgb, _Alpha);
            }
            ENDCG
        }
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _Alpha;
            v2f vert (appdata_base v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

X 射线效果实现

Shader "ShaderProj/16/OcclusionTransparent_X"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        // 菲涅尔反射颜色
        _FresnelScale ("FresnelScale", Range(0,2)) = 1
        _FresnelN ("FresnelN", Range(0, 5)) = 5
        _Color ("Color", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        Pass
        {
            ZTest Greater
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct v2f
            {
                float4 vertex : SV_POSITION;
                float3 worldViewDir : TEXCOORD0;
                float3 worldNormal : TEXCOORD1;
            };
            fixed _FresnelN;
            fixed _FresnelScale;
            fixed4 _Color;
            v2f vert (appdata_base v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldViewDir = normalize(WorldSpaceViewDir(v.vertex));
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                fixed alpha = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(normalize(i.worldViewDir), normalize(i.worldNormal)), _FresnelN);
                return fixed4(_Color.rgb, alpha);
            }
            ENDCG
        }
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed _Alpha;
            v2f vert (appdata_base v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号