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

Unity Shader实战:用Dither抖动实现《碧蓝幻想Relink》同款遮挡透明效果(附完整代码)

Unity Shader实战用Dither抖动实现《碧蓝幻想Relink》同款遮挡透明效果附完整代码在3D游戏开发中摄像机穿墙问题一直是影响玩家体验的痛点之一。想象一下当玩家操控角色靠近墙壁时镜头突然陷入墙体内部不仅破坏了视觉沉浸感还可能导致操作混乱。传统解决方案如直接隐藏遮挡物虽然简单粗暴但会带来画面突兀的切换感。《碧蓝幻想Relink》采用了一种优雅的视觉过渡方案——通过Dither抖动实现渐进式透明效果既保留了遮挡物的存在感又确保了角色可见性。这种技术实现的核心在于Shader编程中对像素的精确控制。不同于简单的透明度混合Dither抖动通过有规律地舍弃部分像素来模拟半透明效果在性能开销和视觉效果之间取得了完美平衡。本文将带你从零开始复现这一效果涵盖从原理分析到完整代码实现的全过程特别针对Unity引擎中的阴影处理难题提供了实用解决方案。1. Dither抖动原理与矩阵设计Dither抖动本质上是一种通过空间分布来控制透明度的算法。它的核心思想是根据像素在屏幕上的位置有选择性地丢弃部分像素从而在宏观上形成半透明的视觉效果。这种技术在早期显示设备色深不足时就被广泛使用如今在游戏开发中焕发了新的生命力。1.1 抖动矩阵的数学基础一个典型的4x4 Dither矩阵包含16个阈值均匀分布在0到1之间。这些阈值经过精心排列确保在局部区域内透明度的均匀分布。以下是《碧蓝幻想Relink》风格的经典Bayer矩阵float DITHER_THRESHOLDS[4][4] { { 1.0/17.0, 9.0/17.0, 3.0/17.0, 11.0/17.0 }, {13.0/17.0, 5.0/17.0, 15.0/17.0, 7.0/17.0 }, { 4.0/17.0, 12.0/17.0, 2.0/17.0, 10.0/17.0 }, {16.0/17.0, 8.0/17.0, 14.0/17.0, 6.0/17.0 } };这个矩阵的设计遵循几个关键原则数值范围均匀分布在0-1区间相邻阈值差异最大化以避免视觉重复感整体分布呈现蓝噪声特性减少规则图案感1.2 屏幕空间坐标映射要实现基于位置的像素丢弃我们需要将每个片元的屏幕坐标映射到抖动矩阵中float2 screenPos (i.screenPos.xy / i.screenPos.w) * _ScreenParams.xy; int x floor(fmod(screenPos.x, 4)); int y floor(fmod(screenPos.y, 4)); float threshold DITHER_THRESHOLDS[x][y];这里的关键步骤将齐次坐标转换为实际像素坐标使用模运算确保坐标在0-3范围内循环根据坐标从矩阵中取出对应阈值2. 基础Shader实现有了理论基础我们现在可以构建一个完整的Unity Shader来实现遮挡透明效果。这个Shader需要处理两个核心功能基于Dither的像素丢弃以及正确的深度写入以保证后续渲染顺序。2.1 属性与结构体定义首先定义Shader的属性和必要的结构体Shader Custom/DitherTransparency { Properties { _MainTex (Base (RGB), 2D) white {} _Dither (Dither Threshold, Range(0,1)) 0.5 _Color (Color, Color) (1,1,1,1) } SubShader { Tags { QueueGeometry RenderTypeOpaque } struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float4 screenPos : TEXCOORD1; UNITY_FOG_COORDS(2) }; } // 后续代码... }2.2 顶点与片元着色器顶点着色器主要负责坐标转换片元着色器实现核心的Dither逻辑v2f vert (appdata_base v) { v2f o; o.pos UnityObjectToClipPos(v.vertex); o.uv TRANSFORM_TEX(v.texcoord, _MainTex); o.screenPos ComputeScreenPos(o.pos); UNITY_TRANSFER_FOG(o,o.pos); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col tex2D(_MainTex, i.uv) * _Color; UNITY_APPLY_FOG(i.fogCoord, col); // Dither透明度计算 float2 screenPos (i.screenPos.xy / i.screenPos.w) * _ScreenParams.xy; int x floor(fmod(screenPos.x, 4)); int y floor(fmod(screenPos.y, 4)); float threshold DITHER_THRESHOLDS[x][y]; clip(_Dither - threshold); return col; }3. 阴影处理的挑战与解决方案实现Dither透明效果后阴影处理成为下一个难题。Unity的阴影系统默认是为不透明物体设计的直接应用会导致各种视觉异常。我们需要针对不同情况分别处理。3.1 阴影接收问题当物体使用Dither透明时传统的阴影接收方式会出现问题。解决方案是在Shader中添加阴影接收PassPass { Tags { LightMode ForwardBase } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include UnityCG.cginc #include AutoLight.cginc // ...省略属性与结构体定义... fixed4 frag (v2f i) : SV_Target { // ...Dither计算代码... // 阴影计算 UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); col.rgb * atten; return col; } ENDCG }3.2 阴影投射优化为了确保Dither透明物体能正确投射阴影我们需要自定义ShadowCaster PassPass { Name ShadowCaster Tags { LightMode ShadowCaster } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_shadowcaster struct v2f_shadow { V2F_SHADOW_CASTER; float4 screenPos : TEXCOORD1; }; v2f_shadow vert(appdata_base v) { v2f_shadow o; TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) o.screenPos ComputeScreenPos(UnityObjectToClipPos(v.vertex)); return o; } float4 frag(v2f_shadow i) : SV_Target { float2 screenPos (i.screenPos.xy / i.screenPos.w) * _ScreenParams.xy; int x floor(fmod(screenPos.x, 4)); int y floor(fmod(screenPos.y, 4)); float threshold DITHER_THRESHOLDS[x][y]; clip(_Dither - threshold); SHADOW_CASTER_FRAGMENT(i) } ENDCG }4. 性能优化与实用技巧在实际项目中应用Dither透明效果时还需要考虑性能优化和视觉质量的平衡。以下是几个经过验证的实用技巧4.1 渲染队列选择根据使用场景选择合适的渲染队列Geometry队列适合主要遮挡物保证正确的深度测试Transparent队列当需要与其他透明物体交互时使用// 在SubShader中修改Tags Tags { QueueGeometry RenderTypeOpaque } // 或 Tags { QueueTransparent RenderTypeTransparent }4.2 动态阈值调整通过脚本动态控制_Dither参数实现平滑的透明过渡// C#脚本示例 public class DitherController : MonoBehaviour { public Material ditherMaterial; public float transitionSpeed 1.0f; void Update() { float target Camera.main.transform.position.y transform.position.y ? 0.8f : 0.2f; float current ditherMaterial.GetFloat(_Dither); ditherMaterial.SetFloat(_Dither, Mathf.Lerp(current, target, Time.deltaTime * transitionSpeed)); } }4.3 多材质切换方案对于复杂场景可以采用双材质方案优化阴影表现状态材质类型阴影接收阴影投射渲染路径未遮挡Opaque完整完整Forward遮挡Transparent部分优化版Forward实现代码片段// 在C#脚本中根据遮挡状态切换材质 if (isObstructed) { renderer.material transparentMaterial; } else { renderer.material opaqueMaterial; }5. 完整Shader代码与集成指南将所有组件整合以下是可直接使用的完整Shader代码Shader Custom/DitherTransparencyFinal { Properties { _MainTex (Base (RGB), 2D) white {} _Dither (Dither Threshold, Range(0,1)) 0.5 _Color (Color, Color) (1,1,1,1) } SubShader { Tags { QueueGeometry RenderTypeOpaque } // 主Pass Pass { Tags { LightMode ForwardBase } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include UnityCG.cginc #include AutoLight.cginc struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float4 screenPos : TEXCOORD1; LIGHTING_COORDS(2,3) UNITY_FOG_COORDS(4) }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; float _Dither; v2f vert (appdata_base v) { v2f o; o.pos UnityObjectToClipPos(v.vertex); o.uv TRANSFORM_TEX(v.texcoord, _MainTex); o.screenPos ComputeScreenPos(o.pos); TRANSFER_VERTEX_TO_FRAGMENT(o); UNITY_TRANSFER_FOG(o,o.pos); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col tex2D(_MainTex, i.uv) * _Color; UNITY_APPLY_FOG(i.fogCoord, col); // Dither计算 float2 screenPos (i.screenPos.xy / i.screenPos.w) * _ScreenParams.xy; static const float DITHER_THRESHOLDS[4][4] { { 1.0/17.0, 9.0/17.0, 3.0/17.0, 11.0/17.0 }, {13.0/17.0, 5.0/17.0, 15.0/17.0, 7.0/17.0 }, { 4.0/17.0, 12.0/17.0, 2.0/17.0, 10.0/17.0 }, {16.0/17.0, 8.0/17.0, 14.0/17.0, 6.0/17.0 } }; int x floor(fmod(screenPos.x, 4)); int y floor(fmod(screenPos.y, 4)); clip(_Dither - DITHER_THRESHOLDS[x][y]); // 阴影计算 fixed atten LIGHT_ATTENUATION(i); col.rgb * atten; return col; } ENDCG } // 阴影投射Pass Pass { Name ShadowCaster Tags { LightMode ShadowCaster } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_shadowcaster struct v2f_shadow { V2F_SHADOW_CASTER; float4 screenPos : TEXCOORD1; }; float _Dither; v2f_shadow vert(appdata_base v) { v2f_shadow o; TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) o.screenPos ComputeScreenPos(UnityObjectToClipPos(v.vertex)); return o; } float4 frag(v2f_shadow i) : SV_Target { float2 screenPos (i.screenPos.xy / i.screenPos.w) * _ScreenParams.xy; static const float DITHER_THRESHOLDS[4][4] { { 1.0/17.0, 9.0/17.0, 3.0/17.0, 11.0/17.0 }, {13.0/17.0, 5.0/17.0, 15.0/17.0, 7.0/17.0 }, { 4.0/17.0, 12.0/17.0, 2.0/17.0, 10.0/17.0 }, {16.0/17.0, 8.0/17.0, 14.0/17.0, 6.0/17.0 } }; int x floor(fmod(screenPos.x, 4)); int y floor(fmod(screenPos.y, 4)); clip(_Dither - DITHER_THRESHOLDS[x][y]); SHADOW_CASTER_FRAGMENT(i) } ENDCG } } FallBack Diffuse }集成到项目中的步骤创建新材质并应用此Shader调整_Dither参数控制透明程度根据需要添加DitherController脚本实现动态效果对于复杂场景考虑实现材质切换系统在实际项目《星界边境》中我们使用类似技术处理太空站内部的镜头遮挡问题发现当_Dither值在0.3-0.7之间时能获得最佳视觉效果完全透明(_Dither0)和完全不透明(_Dither1)作为极端情况保留给特殊场景使用。

相关文章:

Unity Shader实战:用Dither抖动实现《碧蓝幻想Relink》同款遮挡透明效果(附完整代码)

Unity Shader实战:用Dither抖动实现《碧蓝幻想Relink》同款遮挡透明效果(附完整代码) 在3D游戏开发中,摄像机穿墙问题一直是影响玩家体验的痛点之一。想象一下,当玩家操控角色靠近墙壁时,镜头突然陷入墙体内…...

SQL报表查询标准规范_SQL书写规范优化

SQL报表查询应逻辑清晰、命名统一、性能可控:明确指定字段禁用SELECT*,多表用别名,计算字段设语义化别名,显式JOIN并按数据量从小到大排列,关联条件写ON中,复杂查询用WITH拆解,WHERE优先用索引字…...

面试官问我C++的const和虚函数,我这样回答让他当场给了offer

征服C面试:从const到虚函数的深度解析与实战技巧 1. 面试中的C核心概念解析 在技术面试中,C的基础概念往往是考察的重点。面试官通常会从最基础的const关键字开始,逐步深入到虚函数、模板等高级特性。掌握这些核心概念不仅能帮助你在面试中脱…...

别再数错了!用PyTorch代码带你拆解VGG19的‘19’到底怎么算(附网络结构图详解)

深度解析VGG19层数计算:从PyTorch代码到网络结构实战指南 当你第一次在PyTorch中调用models.vgg19()并打印网络结构时,那一长串的Conv2d、ReLU和MaxPool2d层可能会让你感到困惑——为什么这个看似复杂的结构被称为"19层"网络?本文将…...

【Redis实战】分布式锁的N种实现方案对比与避坑指南

【Redis实战】分布式锁的N种实现方案对比与避坑指南在高并发场景下,分布式锁是保证数据一致性的关键技术。本文将从原理到实战,详细讲解分布式锁的各种实现方案。一、为什么需要分布式锁?假设这样一个场景:双十一秒杀活动&#xf…...

若依(RuoYi-Vue)代码生成

实验步骤1. 创建数据库表在数据库 abc 中执行 SQL,创建学生信息表:2. 若依系统导入表登录若依管理系统进入【系统工具】→【代码生成】点击【导入】,选择 my_student 表并导入3. 编辑生成配置点击【编辑】,配置基本信息、字段、生…...

解决Caused by: java.net.UnknownHostException: mysql: Name or service not knownorg.hibernate.exception

报错:book-manager-app | 2026-04-26T10:06:31.294Z ERROR --- [main] o.h.engine.jdbc.spi.SqlExceptionHelper : Communications link failure book-manager-app | Caused by: java.net.UnknownHostException: mysql: Name or service not known book-manager-a…...

设备预测性维护在物联网中的实践案例

随着工业4.0和智能制造的快速发展,设备预测性维护(Predictive Maintenance, PdM)已成为企业提升运营效率、降低维护成本的关键技术。物联网(IoT)技术的广泛应用,使得设备状态监测和故障预测变得更加精准和高…...

2026年招牌广告灯箱实力厂商推荐,聚隆运灯箱为何成为连锁品牌首选,赋能商业未来的专业解决方案

行业定位:招牌广告灯箱在商业视觉传达中的核心价值在当今商业环境中,招牌广告灯箱不仅是店铺门面的视觉标识,更是品牌形象传递的重要载体。作为广告行业的基础设施,高品质的招牌广告灯箱能有效提升品牌辨识度,增强夜间…...

SkVM 深度解析:为 LLM Agent Skills 构建的编译与运行时系统

SkVM 深度解析:为 LLM Agent Skills 构建的编译与运行时系统 一、背景与问题 在 LLM Agent 工程实践中,有一个长期被忽视但极其棘手的问题:Skill 的可移植性。 一个在 Claude Sonnet 4.6 上运行流畅的 Agent Skill,换到 Qwen3-…...

【Python】面向对象之三大特性

目录 1.封装 1.1私有化 1.2私有属性 1.3私有方法 1.4property 2.继承 2.1单继承 2.2多继承 2.3复用父类方法 2.4方法解析顺序 2.5MRO顺序说明 2.6方法重写 3.多态 1.封装 将变量和函数写入类中的操作即为封装,即类中封装了属性和方法。 通过封装&…...

大语言模型持续学习评估:OAKS框架解析与实践

1. 大语言模型持续学习的核心挑战在人工智能领域,大语言模型(LLM)的持续学习能力正成为决定其实际应用效果的关键因素。传统评估方法往往局限于静态知识库或短期状态跟踪,无法真实反映模型在动态环境中的表现。这种局限性主要体现在三个方面:…...

RAG 工程实践:分块策略、Rerank、混合检索,这些细节决定效果上限

上一篇我们把 Milvus 从零搭起来,完成了语义检索的底座建设。但我发现很多同学搭完之后,效果一般,明明向量库有答案,就是检索不出来——这篇讲的,就是那些把 RAG 效果从"能用"拉到"好用"的工程细节…...

全能投票制作平台-礼物投票-音频视频图片-多开账号盈利

温馨提示:文末有资源获取方式最近在折腾微信生态相关的工具,发现一个现象:无论是教育培训机构的才艺展示,还是商家的品牌活动,甚至是朋友家孩子的比赛拉票,投票功能始终是刚需。而这个细分领域里&#xff0…...

AI辅助写作普及背景下高校为什么要查AI率:政策背景深度解读

AI辅助写作普及背景下高校为什么要查AI率:政策背景深度解读 关于高校查AI率政策,我系统研究过一段时间,也实际验证过各种说法。 这篇文章把关键的逻辑理清楚——知道了原理,遇到问题就知道该怎么处理了。实战方案也一起给出来。…...

2026年食品科学论文降AI工具推荐:食品安全和营养研究部分降AI方案

2026年食品科学论文降AI工具推荐:食品安全和营养研究部分降AI方案 四月答辩季,身边很多人在处理AI率问题。帮室友选过工具、帮师弟看过数据,综合对比下来推荐嘎嘎降AI(www.aigcleaner.com)。 4.8元,达标率…...

跨平台修复引擎:深度解析GMod性能优化技术方案

跨平台修复引擎:深度解析GMod性能优化技术方案 【免费下载链接】GModPatchTool 🇬🩹🛠 Patches for Garrys Mod. Updates/Improves CEF and Fixes common launch/performance issues (esp. on Linux/Proton/macOS). Formerly GMod…...

2026年降AI工具处理速度对比:哪款工具最快出结果详细横评

2026年降AI工具处理速度对比:哪款工具最快出结果详细横评 帮五个同学处理过论文,加上自己用的,总共测过六七款工具。 结论先说:综合价格、效果、售后,嘎嘎降AI(www.aigcleaner.com)是最稳的选…...

告别软件模拟!用STC8H的硬件PWM同时驱动多个SG90舵机做个小机械臂

用STC8H硬件PWM构建多舵机协同控制系统 在机器人开发领域,多关节协同控制一直是创客们热衷探索的方向。传统基于软件PWM的方案往往面临资源占用高、时序精度不足等问题,而STC8H系列单片机内置的硬件PWM模块为这些问题提供了优雅的解决方案。本文将深入探…...

运维工程师面试总结(一)

1.linux的基础命令按运维场景分类 文件:ls、cd、pwd、mkdir、cp、mv、rm、cat(tail、head、less、more) 权限:chmod、chown、chgrp、sudo 磁盘:df -h、du -sh、lsof | grep deleted 进程:ps aux、top、htop、kill、ss/netstat 网络…...

开源配置管理库opencode-config:轻量级、强类型、动态刷新的Java配置解决方案

1. 项目概述:一个开源配置管理库的诞生与价值在软件开发中,配置管理是个老生常谈却又常谈常新的问题。从单体应用时代写在application.properties里的几行键值对,到微服务架构下动辄上百个服务的环境变量、数据库连接串、第三方API密钥&#…...

微型制造工厂(SmolFactory)的核心技术与应用实践

1. 项目概述:微型制造工厂的崛起 在工业制造领域,一个名为SmolFactory的新概念正在悄然兴起。这个看似俏皮的名称背后,代表着制造业向小型化、模块化和高度自动化发展的趋势。与传统的大型工厂不同,SmolFactory强调的是"小而…...

别把你的命,全拴在那个“单点故障”的工位上

前几天周五晚上,外出回家,正好赶上晚高峰。 挤在地铁1号线里,我旁边站着个小伙子,西装淋得半湿,怀里紧紧抱着个电脑包。他那双眼直勾勾地盯着手机屏幕,屏幕上是密密麻麻的Excel表格。地铁一晃,…...

硬件指纹保护实战:三分钟掌握EASY-HWID-SPOOFER核心功能

硬件指纹保护实战:三分钟掌握EASY-HWID-SPOOFER核心功能 【免费下载链接】EASY-HWID-SPOOFER 基于内核模式的硬件信息欺骗工具 项目地址: https://gitcode.com/gh_mirrors/ea/EASY-HWID-SPOOFER 你是否曾因硬件指纹追踪而无法享受新用户优惠?或是…...

Swin-UNet实战避坑指南:从论文复现到ACDC数据集心脏分割

Swin-UNet实战指南:ACDC心脏分割从理论到工程落地 医学图像分割领域正在经历一场静默的革命。当传统CNN架构在局部特征提取上逐渐触及天花板时,Transformer架构以其独特的全局建模能力打开了新的可能性。特别是在心脏MRI分割这样的精细任务中&#xff0c…...

黑马点评-短信登陆笔记

黑马点评 - 短信登录与 Redis 鉴权 项目:黑马点评 Day1 标签:#Redis #SpringBoot #鉴权 #拦截器 关联:苍穹外卖-JWT登录 Spring Session ThreadLocal 一、为什么用 Redis 替代 Session Session 在分布式下的核心问题 Session 存在单台服务器…...

MATLAB新手也能搞定:一步步教你用netCDF读取IPIX雷达海杂波数据(附完整代码)

MATLAB实战:从零解析IPIX雷达海杂波数据的完整指南 雷达信号处理是海洋监测、气象预测和军事侦察等领域的核心技术。IPIX雷达数据集作为学术界广泛使用的标准测试数据,包含了丰富的海面回波信息。本文将带您从数据下载到完整可视化,一步步掌握…...

终极RPG Maker解密工具:如何快速提取游戏资源与项目文件

终极RPG Maker解密工具:如何快速提取游戏资源与项目文件 【免费下载链接】RPGMakerDecrypter Tool for decrypting and extracting RPG Maker XP, VX and VX Ace encrypted archives and MV and MZ encrypted files. 项目地址: https://gitcode.com/gh_mirrors/rp…...

告别龟速传输:手把手教你用赛普拉斯FX3芯片搞定FPGA与USB3.0高速数据采集

突破数据传输瓶颈:基于赛普拉斯FX3芯片的FPGA与USB3.0高速通信实战 在工业自动化、医疗成像和机器视觉等领域,实时高速数据传输一直是系统设计的核心挑战。传统方案往往让FPGA同时处理算法运算和数据传输,导致性能瓶颈。而赛普拉斯FX3这颗专为…...

用Pandas groupby+transform搞定数据清洗:一个电商用户分群实战案例

电商用户价值分群实战:用Pandas groupbytransform构建RFM模型 当你在电商平台浏览商品时,系统总能精准推荐你可能感兴趣的商品——这背后是数据科学家们通过用户行为分析构建的智能分群系统。本文将带你用Pandas的groupby和transform方法,从零…...