当前位置: 首页 > news >正文

Chapter8 透明效果——Shader入门精要学习笔记

一、基本概念

  • 在Unity中通常使用两种方法来实现透明效果
    • 透明度测试(无法达到真正的半透明效果)
    • 透明度混合(关闭了深度写入)

透明度测试

  • 基本原理:设置一个阈值,只要片元的透明度小于阈值,就会被舍弃(不会进行任何处理,也不会对颜色缓冲造成影响);否则,就会按照普通的不透明物体来处理(进行深度测试、深度写入等)
  • 效果极端,要么透明,要么完全不透明

透明度混合

  • 基本原理:使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色
  • 需要关闭深度写入,但没有关闭深度测试——深度缓冲是只读的
    • 为什么关闭深度写入:如果开启,半透明物体在不透明物体前面时,半透明物体会挡住不透明物体,实现不了半透明效果
  • 可以实现半透明效果

二、渲染顺序

  • 对于半透明物体和不透明物体,因为关闭了深度写入,所以应该在不透明物体渲染完后再渲染半透明物体
  • 对于两个半透明物体,应该先渲染远处的,再渲染近处的(不准确)

1.渲染引擎的方法

  • 先渲染所有不透明物体,并开启他们的深度测试和深度写入
  • 把半透明物体按他们的距离摄像机的远近进行排序,再按照从后往前的顺序渲染,开启深度测试,关闭深度写入
    • 会有更加复杂的重叠情况,排序困难

2.Unity的渲染顺序

  • 使用SubShader的 Queue 标签来决定我们的模型将归于哪个渲染队列,使用整数索引来表示每个渲染队列,号小的越早被渲染
    在这里插入图片描述
  • 如果想要通过透明度测试来实现效果,代码:
SubShader{Tags{ "Queue" = "AlphaTest"}Pass{...}}
  • 如果想使用透明度混合
SubShader{Tags{ "Queue" = "Transparent"}Pass{ZWrite Off //关闭深度写入...}}

三、透明度测试

  • 基本原理:设置一个阈值,只要片元的透明度小于阈值,就会被舍弃(不会进行任何处理,也不会对颜色缓冲造成影响);否则,就会按照普通的不透明物体来处理(进行深度测试、深度写入等)
  • 通常会在片元着色器中使用 clip函数 进行透明度测试
    • 如果给定参数的任何一个分量是负数,则会舍弃当前像素的输出颜色
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'Shader "Custom/Chapter8-AlphaTest"
{Properties{_Color ("Main Tint", Color) = (1,1,1,1)_MainTex ("Main Tex", 2D) = "white"{}_Cutoff ("Alpha Cutoff", Range(0,1)) = 0.5//用于决定我们调用clip进行透明度测试时使用的判决条件}SubShader{Tags {"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutoff"}Pass{Tags{"LightMode" = "ForwardBase"}CGPROGRAM#pragma vertex vert #pragma fragment frag#include "Lighting.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;fixed _Cutoff;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);//透明度测试clip(texColor.a - _Cutoff);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, 1.0);}ENDCG}}FallBack "Transparent/Cutout/VertexLit"
}
  • _Cutoff (“Alpha Cutoff”, Range(0,1)) = 0.5 用于决定我们调用clip进行透明度测试时使用的判决条件
  • clip(texColor.a - _Cutoff); 做透明度测试,判断texColor.a - _Cutoff是否为负数,负数的纹理颜色为全透明
    在这里插入图片描述

四、透明度混合

  • 基本原理:使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色
  • 需要使用Unity提供的混合命令 Blend
    在这里插入图片描述
  • 本节中使用的是Blend SrcFactor DstFactor来进行混合,将源颜色混合因子SrcFactor设为SrcAlpha,目标颜色混合因子设为OneMinusSrcAlpha
    • 即混合后的新颜色为
    • D s t C o l o r n e w DstColor_{new} DstColornew = SrcAlpha × SrcColor + (1-SrcAlpha) × D s t C o l o r o l d DstColor_{old} DstColorold
  • ZWrite Off 关闭深度写入
Shader "Custom/Chapter8-AlphaBlend"
{Properties{_Color ("Main 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"}Pass{Tags {"LightMode" = "ForwardBase"}ZWrite OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#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"
}

在这里插入图片描述

五、开启深度写入的半透明效果

  • 对于复杂网格的半透明处理,避免错误排序
  • 方法:使用 两个Pass 来渲染模型
    • 第一个开启深度写入,但不输出颜色,仅仅为了把该模型的深度值写入深度缓存中
    • 第二个Pass 进行正常的透明度混合,根据第一个Pass,可以进行像素级别的深度排序
    • 缺点是 多个Pass会造成性能的影响
Shader "Custom/Chapter8-AlphaBlend"
{Properties{_Color ("Main 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"}Pass{ZWrite On ColorMask 0}Pass{Tags {"LightMode" = "ForwardBase"}ZWrite OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#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"
}
  • 与透明度混合代码几乎一致,只是增加了一个Pass
Pass{ZWrite On ColorMask 0}
  • ZWrite On 打开深度写入,ColorMask 0 意味着Pass不写入任何颜色通道,不会输出任何颜色
    在这里插入图片描述

六、ShaderLab的混合命令

  • 混合与两个操作数有关:源颜色(SourceColor)和目标颜色(DestinationColor)
    • 源颜色指片元着色器产生的颜色 S
    • 目标颜色指从颜色缓冲中读取到的颜色值 D
    • 混合后的颜色 O
    • 包含了 RGBA四个颜色通道

1.混合等式和参数

  • 将S和D进行混合的等式——混合等式,需要两个,一个用于混合RGB,一个用于混合A
  • 设置混合状态时,即设置等式中的 “操作” 和 “因子”
    • 一共有两个等式(分别用于混合RGB和A)
    • 一个等式里有两个因子(一个与S相乘,一个与D相乘)

在这里插入图片描述

  • 第一个命令中只有两个因子——RGB和A通道用相同的两个因子
    在这里插入图片描述
    混合因子可以是哪些值呢?
    在这里插入图片描述

2.混合操作

  • 可以使用 BlendOp BlendOperation命令
    在这里插入图片描述
    在这里插入图片描述
  • 当使用Min和Max操作时,混合因子不起作用的

3.常见的混合类型(效果)

在这里插入图片描述

七、双面渲染的透明效果

  • 观察到其内部结构
  • 可以使用Cull指令来控制需要剔除哪个面的渲染图元
    • Back:背对相机的渲染图元不会被渲染
    • Front:朝向相机的渲染图元不会被渲染
    • Off:关闭剔除功能
Cull Back | Front | Off

1.透明度测试的双面渲染

  • 在Pass中使用 Cull Off 来关闭剔除功能即可
  • 只在 AlphaTest.shader 里Pass中加一句 Cull Off
    在这里插入图片描述

2.透明度混合的双面渲染

  • 因为关闭了深度写入,如果直接加 Cull Off 的话会造成前后面混乱
  • 所以,写了两个Pass (与AlphaBlend.shader一样)
    • 只在Pass中,第一个Pass只渲染背面 ,添加了Cull Front;第二个只渲染正面 添加了Cull Back
    • SubShader是会按照顺序执行Pass
      在这里插入图片描述

相关文章:

  • 【愤怒的小方块案例 Objective-C语言】
  • Java实现数据结构——不带头单链表
  • 墨烯的Java技术栈-数据结构与算法基础-010
  • STM32_实现双线程控制LED交替闪烁(UCOS)
  • 【C语言】常见的字符串函数
  • 雷池WAF+Modsecurity安装防护及系统加固
  • 【Qwen2部署实战】Qwen2初体验:用Transformers打造智能聊天机器人
  • 【面试题】IPS(入侵防御系统)和IDS(入侵检测系统)的区别
  • 人脸特征68点识别 C++
  • 哪吒汽车,正在等待“太乙真人”的拯救
  • 监听 web 容器内的网络请求
  • vCenter VXR01405C ALARM Certificate is about to expire
  • 【python】scikit-learn安装失败No matching distribution found for scikit-learn
  • 【数据库原理】总结(期末版)
  • 机器学习 C++ 的opencv实现SVM图像二分类的训练 (二)【附源码】
  • centos安装java运行环境jdk+tomcat
  • express.js的介绍及使用
  • go append函数以及写入
  • Javascript弹出层-初探
  • Java到底能干嘛?
  • mysql innodb 索引使用指南
  • php面试题 汇集2
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • ReactNativeweexDeviceOne对比
  • 百度小程序遇到的问题
  • 湖南卫视:中国白领因网络偷菜成当代最寂寞的人?
  • 解决iview多表头动态更改列元素发生的错误
  • 如何胜任知名企业的商业数据分析师?
  • 数据可视化之 Sankey 桑基图的实现
  • 一道面试题引发的“血案”
  • 追踪解析 FutureTask 源码
  • Prometheus VS InfluxDB
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • ( 10 )MySQL中的外键
  • (007)XHTML文档之标题——h1~h6
  • (1)Android开发优化---------UI优化
  • (16)Reactor的测试——响应式Spring的道法术器
  • (22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别
  • (C#)一个最简单的链表类
  • (Qt) 默认QtWidget应用包含什么?
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (附源码)计算机毕业设计SSM在线影视购票系统
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (原)本想说脏话,奈何已放下
  • (转)详解PHP处理密码的几种方式
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .net framwork4.6操作MySQL报错Character set ‘utf8mb3‘ is not supported 解决方法
  • .NET MVC之AOP
  • @ConditionalOnProperty注解使用说明
  • @kafkalistener消费不到消息_消息队列对战之RabbitMq 大战 kafka