• 【图形学】27 透明度混合


    来源:《UNITY SHADER入门精要》

    1、Unity实现透明度混合

      正如之前所说,为了实现透明度混合,我们必须在绘制半透明物体的时候关闭 深度值写入。Unity已经给我们提供了混合的命令:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7SgNCtif-1663118310572)(assets/image-20220624214702272.png)]

      我们书中使用的是表中第二种语义,我们会把 SrcFactor 设为 SrcAlpha,而目标颜色的混合因子 DstFactor 设为 OneMinusSrcAlpha。那么,混合之后的颜色是:
    D s t C o l o r n e w = S r c A l p h a × S r c C o l o r + ( 1 − S r c A l p h a ) × D s t C o l o r o l d DstColor_{new}=SrcAlpha \times SrcColor + (1 - SrcAlpha) \times DstColor_{old} DstColornew=SrcAlpha×SrcColor+(1SrcAlpha)×DstColorold

    2、代码学习

    Shader "Unity Shaders Book/Chapter 8/Alpha Blend" {
    	Properties {
    		_Color ("Color Tint", Color) = (1, 1, 1, 1)
    		_MainTex ("Main Tex", 2D) = "white" {}
    		_AlphaScale ("Alpha Scale", Range(0, 1)) = 1
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      我们新增了一个 _AlphaScale来控制透明度的变化。

    	SubShader {
    		Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
    		
    		Pass {
    			Tags { "LightMode"="ForwardBase" }
    
    • 1
    • 2
    • 3
    • 4
    • 5

      SubShader 的 Tags 中,设置了 "Queue"="Transparent" 使用了Transparent 的渲染队列,RenderType 标签可以让 Unity 把这个 Shader 归入到提前定义的组里面(这里就是 Transparent 组)中,用来指明该 Shader 是一个使用了透明度混合的 Shader。别忘了我们还 设置了 "IgnoreProjector"="True",来禁止被投影。
      当然 Pass 中光照还是设置为 ForwardBase。

    			ZWrite Off
    			Blend SrcAlpha OneMinusSrcAlpha
    
    • 1
    • 2

      Pass中采用 Blend 命令

    			CGPROGRAM
    			
    			#pragma vertex vert
    			#pragma fragment frag
    			
    			#include "Lighting.cginc"
    			
    			fixed4 _Color;
    			sampler2D _MainTex;
    			float4 _MainTex_ST;
    			fixed _AlphaScale;
    
    			struct a2v {
    				float4 vertex : POSITION;
    				float3 normal : NORMAL;
    				float4 texcoord : TEXCOORD0;
    			};
    			
    			struct v2f {
    				float4 pos : SV_POSITION;
    				float3 worldNormal : TEXCOORD0;
    				float3 worldPos : TEXCOORD1;
    				float2 uv : TEXCOORD2;
    			};
    			
    			v2f vert(a2v v) {
    				v2f o;
    				o.pos = UnityObjectToClipPos(v.vertex);
    				
    				o.worldNormal = UnityObjectToWorldNormal(v.normal);
    				
    				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    				
    				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    				
    				return o;
    			}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

      emmmm维持不变,没啥好说的。

    			fixed4 frag(v2f i) : SV_Target {
    				fixed3 worldNormal = normalize(i.worldNormal);
    				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
    				
    				fixed4 texColor = tex2D(_MainTex, i.uv);
    				
    				fixed3 albedo = texColor.rgb * _Color.rgb;
    				
    				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
    				
    				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
    				
    				return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
    			}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

      唯一变动的部分就是第 13 行,返回的时候,最后一项,用 texColor.a * AlphaScale 构造了最后一项透明度。其他的,因为我们开启了 Blend,Unity会帮我们自动完成。

    			ENDCG
    		}
    	} 
    	FallBack "Transparent/VertexLit"
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3、透明度混合可能存在的问题

      如果一个模型本身就有复杂的结构和网格,就会因为各种排序的问题而且深度值关闭,导致成像有问题:

      所以,我们打算采用开启深度值的方法写入半透明效果。我们使用两个 Pass 来渲染模型:第一个 Pass 开启深度写入,但不输出颜色,仅仅是将在最表面的物体写入深度缓冲中。第二个 Pass 就直接依据上一个 Pass 深度值排序的结果来进行渲染。

    4、开启深度写入的透明度混合

    Shader "Unity Shaders Book/Chapter 8/Alpha Blending With ZWrite" {
    	Properties {
    		_Color ("Color Tint", Color) = (1, 1, 1, 1)
    		_MainTex ("Main Tex", 2D) = "white" {}
    		_AlphaScale ("Alpha Scale", Range(0, 1)) = 1
    	}
    	SubShader {
    		Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
    		
    		// Extra pass that renders to depth buffer only
    		Pass {
    			ZWrite On
    			ColorMask 0
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

      后面的代码都和前面是一样的,就这里因为多定义了一个Pass,所以不一样了一些,这里 ZWrite On 开启了深度值测试,而后用 ColorMask 0 又关闭了颜色输出。

      这里的 ColorMask 的语义是:

    ColorMask RGB \ A \ 0 \ 其它任何 R、G、B、A 的任意组合

    		Pass {
    			Tags { "LightMode"="ForwardBase" }
    			
    			ZWrite Off
    			Blend SrcAlpha OneMinusSrcAlpha
    			
    			CGPROGRAM
    			
    			#pragma vertex vert
    			#pragma fragment frag
    			
    			#include "Lighting.cginc"
    			
    			fixed4 _Color;
    			sampler2D _MainTex;
    			float4 _MainTex_ST;
    			fixed _AlphaScale;
    			
    			struct a2v {
    				float4 vertex : POSITION;
    				float3 normal : NORMAL;
    				float4 texcoord : TEXCOORD0;
    			};
    			
    			struct v2f {
    				float4 pos : SV_POSITION;
    				float3 worldNormal : TEXCOORD0;
    				float3 worldPos : TEXCOORD1;
    				float2 uv : TEXCOORD2;
    			};
    			
    			v2f vert(a2v v) {
    				v2f o;
    				o.pos = UnityObjectToClipPos(v.vertex);
    				
    				o.worldNormal = UnityObjectToWorldNormal(v.normal);
    				
    				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    				
    				o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
    				
    				return o;
    			}
    			
    			fixed4 frag(v2f i) : SV_Target {
    				fixed3 worldNormal = normalize(i.worldNormal);
    				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
    				
    				fixed4 texColor = tex2D(_MainTex, i.uv);
    				
    				fixed3 albedo = texColor.rgb * _Color.rgb;
    				
    				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
    				
    				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
    				
    				return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
    			}
    			
    			ENDCG
    		}
    	} 
    	FallBack "Transparent/VertexLit"
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
  • 相关阅读:
    LeetCode每日一题(1012. Numbers With Repeated Digits)
    真的?听声音就能识别新冠患者?DeepMind海龟面部识别AI实践;数据建模与数据库·最新课程;推荐系统快速实现框架;前沿论文 | ShowMeAI资讯日报
    有哪些好用的IT资产管理平台?
    排序和RMQ
    winRAR常用命令
    C++基础——数组
    【UI】element-ui的el-dialog的遮罩层在模态框的前面bug
    Xception:使用Tensorflow从头开始实现
    C++ 智能指针
    6个步骤实现 Postman 接口压力测试
  • 原文地址:https://blog.csdn.net/qq_40891541/article/details/126845880