6.遮挡半透明效果-具体实现
6.1 知识点
知识回顾:基本原理
两个 Pass 渲染对象:
- 一个 Pass 用于渲染被遮挡部分
- 一个 Pass 用于渲染未遮挡模型
被遮挡部分通过修改深度测试规则为“大于”并且关闭深度写入来达到目的。
Schlick 菲涅耳近似等式:
R(θ) = R0 + (1 − R0) · (1 − V·N)^5
其中:
- R(θ) 表示入射角为 θ 时的反射率
- R0 是垂直入射某介质时的反射率
- V 是视角方向的单位向量(入射角)
- N 是顶点法线的单位向量
遮挡半透明效果的实现 —— 实现基础半透明效果
主要步骤
- 新建 Shader(命名为 OcclusionTransparent1),删除无用代码
- 声明属性并映射属性,包括主纹理和透明度
- 复制一个 Pass
- 第一个 Pass 实现:
- 关键点:将深度测试修改为“大于”(
ZTest Greater
),关闭深度写入(ZWrite Off
),采用传统透明混合因子(Blend SrcAlpha OneMinusSrcAlpha
,即混合后的颜色 = 源颜色×源 Alpha + 目标颜色×(1 − 源 Alpha)) - 在片元着色器中,利用传统纹理采样实现,返回颜色时使用透明度变量作为透明通道
- 关键点:将深度测试修改为“大于”(
- 第二个 Pass 实现:传统纹理采样
新建 Shader:OcclusionTransparent1,删除无用代码
Shader "Unlit/Lesson06_OcclusionTransparent1"
{
Properties
{
// 主纹理,默认白色
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags
{
// 设置渲染类型为不透明
"RenderType"="Opaque"
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// 顶点到片段的结构体
struct v2f
{
float2 uv : TEXCOORD0; // 纹理坐标
float4 vertex : SV_POSITION; // 顶点位置
};
// 顶点着色器
v2f vert(appdata_base appdata_base)
{
v2f v2f;
v2f.vertex = UnityObjectToClipPos(appdata_base.vertex); // 从模型空间转到裁剪空间
v2f.uv = TRANSFORM_TEX(appdata_base.texcoord, _MainTex); // 应用纹理坐标变换
return v2f;
}
// 片段着色器
fixed4 frag(v2f v2f) : SV_Target
{
}
ENDCG
}
}
}
声明属性及映射(主纹理与透明度)
Properties
{
// 主纹理,默认白色
_MainTex ("Texture", 2D) = "white" {}
// 透明度,范围从 0 到 1
_Alpha("Alpha", Range(0,1)) = 0.5
}
// 着色器参数
sampler2D _MainTex; // 主纹理
float4 _MainTex_ST; // 纹理变换参数
fixed _Alpha; // 透明度
复制 Pass:第一个 Pass 负责遮挡半透明效果,第二个 Pass 负责传统纹理
//遮挡半透明效果纹理
Pass
{
//...
}
//传统纹理
Pass
{
//...
}
第一个 Pass 中,深度测试被设置为“大于”(ZTest Greater
),并且关闭了深度写入(ZWrite Off
),这意味着只有深度值大于当前深度缓冲区的片段才会被绘制。同时,采用传统的透明混合模式(Blend SrcAlpha OneMinusSrcAlpha
),即混合后的颜色等于源颜色乘以源透明度加上目标颜色乘以(1减去源透明度)。在片元着色器中,纹理采样的实现将采样到的颜色直接返回,并通过透明度变量控制最终的透明通道。
//遮挡半透明效果纹理
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 appdata_base)
{
v2f v2f;
v2f.vertex = UnityObjectToClipPos(appdata_base.vertex); // 从模型空间转到裁剪空间
v2f.uv = TRANSFORM_TEX(appdata_base.texcoord, _MainTex); // 应用纹理坐标变换
return v2f;
}
// 片段着色器
fixed4 frag(v2f v2f) : SV_Target
{
// 取样主纹理并返回颜色,应用透明度
fixed4 col = tex2D(_MainTex, v2f.uv); // 从主纹理取样
return fixed4(col.rgb, _Alpha); // 返回 RGB 颜色并应用透明度
}
ENDCG
}
第二个 Pass 使用传统纹理采样实现
//传统纹理
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 appdata_base)
{
v2f v2f;
v2f.vertex = UnityObjectToClipPos(appdata_base.vertex); // 从模型空间转到裁剪空间
v2f.uv = TRANSFORM_TEX(appdata_base.texcoord, _MainTex); // 应用纹理坐标变换
return v2f;
}
// 片段着色器
fixed4 frag(v2f v2f) : SV_Target
{
// 取样主纹理并直接返回颜色
fixed4 col = tex2D(_MainTex, v2f.uv); // 从主纹理取样
return col; // 返回 RGB 颜色
}
ENDCG
}
最终应用
赋值 shader 和材质后,将其应用到一个球体上,再在该球体前放置一个立方体,即可观察到被遮挡部分同样呈现半透明效果。
遮挡半透明效果的实现 —— 实现 X 射线半透明效果
主要步骤
- 新建 Shader(命名为 OcclusionTransparent2),删除无用代码
- 声明属性并映射属性,包括:
- 主纹理
- 自定义颜色
- 菲涅耳反射率
- 菲涅耳 N 次方
- 复制一个 Pass
- 第一个 Pass 实现:
- 关键点:将深度测试修改为“大于”(
ZTest Greater
),关闭深度写入(ZWrite Off
),采用传统透明混合模式(Blend SrcAlpha OneMinusSrcAlpha
) - 在结构体中包含顶点、世界空间法线及世界空间视角方向
- 顶点着色器进行顶点变换,并计算得到世界空间法线和视角方向
- 片段着色器利用菲涅耳公式结合自定义参数计算,将计算结果作为自定义颜色的透明通道值
- 关键点:将深度测试修改为“大于”(
- 第二个 Pass 实现:传统纹理采样
复制基础半透明效果效果Shader,因为他们非常类似,可以在他的基础上修改
Shader "Unlit/Lesson06_OcclusionTransparent2"
{
//...
}
声明属性,删除透明度,包括主纹理,自定义颜色,菲涅尔反射率,菲涅尔n次方
Properties
{
// 主纹理,默认白色
_MainTex ("Texture", 2D) = "white" {}
// 菲涅耳反射的垂直角度反射率
_FresnelScale("FresnelScale", Range(0,2)) = 1
// 菲涅耳反射近似公式中的 N 次方
_FresnelN("FresnelN", Range(0,5)) = 5
// 自定义颜色,默认白色
_Color("Color", Color) = (1,1,1,1)
}
复制一个Pass,第一个Pass负责遮挡X射线效果,第二个Pass负责传统纹理
//遮挡实现X射线半透明效果纹理
Pass
{
//...
}
//传统纹理
Pass
{
//...
}
第一个 Pass 中,深度测试被设置为“大于”(ZTest Greater
),并且关闭了深度写入(ZWrite Off
),确保只有深度值大于当前深度缓冲区的片段才会被渲染。同时,使用了传统的透明混合模式(Blend SrcAlpha OneMinusSrcAlpha
),即混合后的颜色是源颜色乘以源透明度再加上目标颜色乘以(1减去源透明度)。在结构体中,包含了顶点、世界空间法线和世界空间视角方向。顶点着色器负责进行顶点变换,并计算得到世界空间法线和视角方向,而在片元着色器中,利用菲涅尔公式结合自定义参数进行计算,最终将计算结果作为自定义颜色的透明通道值。
//遮挡实现X射线半透明效果纹理
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; // 世界空间下法线方向
};
// 透明度计算所需的参数 X射线Pass中不需要才有 所以不需要主纹理
fixed _FresnelScale; // 菲涅尔反射的垂直角度反射率
fixed _FresnelN; // 菲涅尔反射公式中的 N 次方
fixed4 _Color; // 自定义颜色
// 顶点着色器
v2f vert(appdata_base appdata_base)
{
v2f v2f;
// 从模型空间转换到裁剪空间
v2f.vertex = UnityObjectToClipPos(appdata_base.vertex);
// 计算视角方向和法线方向
v2f.worldViewDir = normalize(WorldSpaceViewDir(appdata_base.vertex)); // 世界空间中的视角方向
v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal); // 世界空间中的法线
return v2f;
}
// 片段着色器
fixed4 frag(v2f v2f) : SV_Target
{
//Schlick 菲涅耳近似等式
//R(θ) = R0 + (1− R0 )(1 − V·𝑵)^𝟓
//其中:
//R(θ) 表示入射角为θ时时的反射率
//R0 是垂直入射某介质时的反射率
//V 是视角方向单位向量(入射角)
//N 是顶点法线单位向量
// 使用菲涅尔反射公式计算 alpha(透明度)
fixed alpha = _FresnelScale +
(1 - _FresnelScale)
* pow(1 - dot(normalize(v2f.worldViewDir), normalize(v2f.worldNormal)), _FresnelN);
// 返回颜色值并应用透明度
return fixed4(_Color.rgb, alpha);
}
ENDCG
}
第二个 Pass 使用传统纹理采样实现
//传统纹理
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 appdata_base)
{
v2f v2f;
v2f.vertex = UnityObjectToClipPos(appdata_base.vertex); // 从模型空间转到裁剪空间
v2f.uv = TRANSFORM_TEX(appdata_base.texcoord, _MainTex); // 纹理坐标变换
return v2f;
}
// 片段着色器
fixed4 frag(v2f v2f) : SV_Target
{
// 取样主纹理并返回颜色
fixed4 col = tex2D(_MainTex, v2f.uv);
return col;
}
ENDCG
}
最终应用
将 shader 与材质赋值给一个球体,并在该球体前放置一个立方体,即可观察到被遮挡部分也呈现 X 射线半透明效果。
6.2 知识点代码
Lesson06_OcclusionTransparent1.shader
Shader "Unlit/Lesson06_OcclusionTransparent1"
{
Properties
{
// 主纹理,默认白色
_MainTex ("Texture", 2D) = "white" {}
// 透明度,范围从 0 到 1
_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 appdata_base)
{
v2f v2f;
v2f.vertex = UnityObjectToClipPos(appdata_base.vertex); // 从模型空间转到裁剪空间
v2f.uv = TRANSFORM_TEX(appdata_base.texcoord, _MainTex); // 应用纹理坐标变换
return v2f;
}
// 片段着色器
fixed4 frag(v2f v2f) : SV_Target
{
// 取样主纹理并返回颜色,应用透明度
fixed4 col = tex2D(_MainTex, v2f.uv); // 从主纹理取样
return fixed4(col.rgb, _Alpha); // 返回 RGB 颜色并应用透明度
}
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 appdata_base)
{
v2f v2f;
v2f.vertex = UnityObjectToClipPos(appdata_base.vertex); // 从模型空间转到裁剪空间
v2f.uv = TRANSFORM_TEX(appdata_base.texcoord, _MainTex); // 应用纹理坐标变换
return v2f;
}
// 片段着色器
fixed4 frag(v2f v2f) : SV_Target
{
// 取样主纹理并直接返回颜色
fixed4 col = tex2D(_MainTex, v2f.uv); // 从主纹理取样
return col; // 返回 RGB 颜色
}
ENDCG
}
}
}
Lesson06_OcclusionTransparent2.shader
Shader "Unlit/Lesson06_OcclusionTransparent2"
{
Properties
{
// 主纹理,默认白色
_MainTex ("Texture", 2D) = "white" {}
// 菲涅尔反射的垂直角度反射率
_FresnelScale("FresnelScale", Range(0,2)) = 1
// 菲涅尔反射近似公式中的 N 次方
_FresnelN("FresnelN", Range(0,5)) = 5
// 自定义颜色,默认白色
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags
{
// 设置渲染类型为不透明
"RenderType"="Opaque"
}
//遮挡实现X射线半透明效果纹理
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; // 世界空间下法线方向
};
// 透明度计算所需的参数 X射线Pass中不需要才有 所以不需要主纹理
fixed _FresnelScale; // 菲涅尔反射的垂直角度反射率
fixed _FresnelN; // 菲涅尔反射公式中的 N 次方
fixed4 _Color; // 自定义颜色
// 顶点着色器
v2f vert(appdata_base appdata_base)
{
v2f v2f;
// 从模型空间转换到裁剪空间
v2f.vertex = UnityObjectToClipPos(appdata_base.vertex);
// 计算视角方向和法线方向
v2f.worldViewDir = normalize(WorldSpaceViewDir(appdata_base.vertex)); // 世界空间中的视角方向
v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal); // 世界空间中的法线
return v2f;
}
// 片段着色器
fixed4 frag(v2f v2f) : SV_Target
{
//Schlick 菲涅耳近似等式
//R(θ) = R0 + (1− R0 )(1 − V·𝑵)^𝟓
//其中:
//R(θ) 表示入射角为θ时时的反射率
//R0 是垂直入射某介质时的反射率
//V 是视角方向单位向量(入射角)
//N 是顶点法线单位向量
// 使用菲涅尔反射公式计算 alpha(透明度)
fixed alpha = _FresnelScale +
(1 - _FresnelScale)
* pow(1 - dot(normalize(v2f.worldViewDir), normalize(v2f.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 appdata_base)
{
v2f v2f;
v2f.vertex = UnityObjectToClipPos(appdata_base.vertex); // 从模型空间转到裁剪空间
v2f.uv = TRANSFORM_TEX(appdata_base.texcoord, _MainTex); // 纹理坐标变换
return v2f;
}
// 片段着色器
fixed4 frag(v2f v2f) : SV_Target
{
// 取样主纹理并返回颜色
fixed4 col = tex2D(_MainTex, v2f.uv);
return col;
}
ENDCG
}
}
}
Lesson06_遮挡半透明效果_具体实现.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson06_遮挡半透明效果_具体实现 : MonoBehaviour
{
void Start()
{
#region 知识回顾 基本原理
//两个Pass渲染对象,一个Pass用于渲染被遮挡部分,一个Pass用于渲染未遮挡模型。
//被遮挡部分通过修改深度测试规则为大于并且关闭深度写入来达到目的
//Schlick 菲涅耳近似等式
//R(θ) = R0 + (1− R0 )(1 − V·𝑵)^𝟓
//其中:
//R(θ) 表示入射角为θ时时的反射率
//R0 是垂直入射某介质时的反射率
//V 是视角方向单位向量(入射角)
//N 是顶点法线单位向量
#endregion
#region 知识点一 遮挡半透明效果的具体实现 —— 实现基础半透明效果
//1.新建Shader 取名 OcclusionTransparent1
// 删除无用改代码
//2.声明属性 映射属性
// 主纹理
// 透明度
//3.复制一个Pass
//4.第一个Pass
// 4-1.关键点 深度测试改为大于 关闭深度写入 传统透明混合因子
// ZTest Greater
// ZWrite Off
// Blend SrcAlpha OneMinusSrcAlpha => 混合后的颜色 = (源颜色*源Alpha)+(目标颜色*(1−源Alpha))
// 4-2.传统纹理采样实现,在片元着色器返回颜色中透明通道用透明度变量
//5.第二个Pass
// 传统纹理采样实现
#endregion
#region 知识点二 遮挡半透明效果的具体实现 —— 实现X射线半透明效果
//1.新建Shader 取名 OcclusionTransparent2
// 删除无用改代码
//2.声明属性 映射属性
// 主纹理
// 自定义颜色
// 菲涅尔反射率
// 菲涅尔n次方
//3.复制一个Pass
//4.第一个Pass
// 4-1.关键点 深度测试改为大于 关闭深度写入 传统透明混合因子
// ZTest Greater
// ZWrite Off
// Blend SrcAlpha OneMinusSrcAlpha => 混合后的颜色 = (源颜色*源Alpha)+(目标颜色*(1−源Alpha))
// 4-2.结构体中
// 顶点、世界空间法线、世界空间视角方向
// 4-3.顶点着色器
// 顶点变换、得到世界空间法线、世界空间视角方向
// 4-4.片元着色器
// 利用菲涅尔公式带入自定义参数计算,将得到的值作为自定义颜色的透明通道值
//5.第二个Pass
// 传统纹理采样实现
#endregion
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com