Cg Programming/Unity/Screen Overlays屏幕叠加

2024-03-11 09:08

本文主要是介绍Cg Programming/Unity/Screen Overlays屏幕叠加,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本教程涵盖了屏幕叠加。

这是关于非标准顶点变换系列教程的第一篇,它偏离了章节“顶点变换”描述的标准顶点变换。这个特别的教程使用了章节“纹理球体”中描述的纹理映射和章节“透明度”中描述的混合。

屏幕叠加

这里写图片描述

有很多屏幕叠加的应用,比如上图中的标题,当然也有其它GUI(图形用户界面)元素如按钮或状态信息。这些元素的共同特征是它们应该总是出现在场景的上面,并且永远不会被其它对象遮挡。这些元素也不应该受到任何摄像机运动的影响。于是,顶点变换应该直接从对象空间变换到屏幕空间。Unity有多种方法将纹理贴图绘制到屏幕的指定位置上。本教程尝试用一个简单的着色器来达到这个目的。

用Cg着色器渲染一张贴图到屏幕上

我们用左下角的XY坐标,屏幕中心点(0, 0)处以像素表示的渲染矩形的左角,以及以像素表示的渲染矩形的WidthHeight来指定贴图的屏幕位置。(指定相对于中心点的坐标通常允许我们支持多种屏幕尺寸和宽高比而不需要进一步调整。)我们使用这些着色器的属性:

Properties {_MainTex ("Texture", Rect) = "white" {}_Color ("Color", Color) = (1.0, 1.0, 1.0, 1.0)_X ("X", Float) = 0.0_Y ("Y", Float) = 0.0_Width ("Width", Float) = 128_Height ("Height", Float) = 128}

相应的uniform变量为:

         uniform sampler2D _MainTex;uniform float4 _Color;uniform float _X;uniform float _Y;uniform float _Width;uniform float _Height;

对于实际对象,我们会使用由两个三角形组成的网格来形成一个矩形。但是,我们也可以使用默认的立方体对象,因为背面剔除(对退化到边缘的三角形的剔除)允许我们确保只有立方体的两个三角形被光栅化。默认立方体对象的角在对象空间中的坐标为-0.5+0.5,即矩形左下角在(-0.5, -0.5)且右上角在(+0.5, +0.5)。为了把这些坐标变换到屏幕空间中用户自定义的坐标,我们首先要把他们变换到屏幕左下角* (0,0)*处以像素表示的光栅化位置上:

  uniform float4 _ScreenParams; // x = width; y = height; // z = 1 + 1.0/width; w = 1 + 1.0/height...vertexOutput vert(vertexInput input) {vertexOutput output;float2 rasterPosition = float2(_X + _ScreenParams.x / 2.0 + _Width * (input.vertex.x + 0.5),_Y + _ScreenParams.y / 2.0 + _Height * (input.vertex.y + 0.5));...

这个变换把我们立方体正面的左下角从对象空间(-0.5,-0.5)变换到光栅化位置float2(_X + _ScreenParams.x / 2.0, _Y + _ScreenParams.y / 2.0),这里_ScreenParams.x是以像素表示的屏幕宽度,_ScreenParams.y是以像素表示的屏幕高度。右上角从(+0.5,+0.5)变换到了float2(_X + _ScreenParams.x / 2.0 + _Width, _Y + _ScreenParams.y / 2.0 + _Height)。光栅位置很实用,并且实际上它经常在OpenGL被使用到。但是,它们不是我们所需要的。

顶点着色器的输出参数在所谓的“裁剪空间”中,如章节“顶点变换”中讨论过的那样。GPU通过在透视除法中除以第四个坐标,把这些坐标变换到-1到1之间的归一化设备坐标。如果我们设置第四个坐标为1,这个除法就没有改变任何东西;于是,我们可以将前三个坐标作为归一化设备坐标中的坐标,这里, (-1,-1,-1)指定了近裁剪平面上屏幕的左下角,(1,1,-1)指定了近裁剪平面的右上角。 为了指定任意坐标作为顶点输出参数,我们必须在这个坐标系统中指定它。幸运地是,把光栅化位置的xy坐标变换到归一化的设备坐标并不是很难。对于坐标z我们希望使用近裁剪平面的坐标。在Unity中,这个取决于平台;因此,我们使用Unity内置的uniform参数_ProjectionParams.y,它指定了近裁剪平面的z坐标。

output.pos = float4(2.0 * rasterPosition.x / _ScreenParams.x - 1.0,2.0 * rasterPosition.y / _ScreenParams.y - 1.0,_ProjectionParams.y, // near plane is at -1.0 or at 0.01.0);

你可以很容易地检查,这个把光栅化位置float2(0, 0)变换到了归一化的设备坐标(-1.0, -1.0),以及把光栅化位置float2(_ScreenParams.x, _ScreenParams.y)变换到(1.0, 1.0),这就是我们想要的。这里还有一个麻烦:有时Unity会使用翻转投影矩阵,它的y轴指向相反的方向。在这种情况下,我们必须用-1乘以y坐标。我们可以通过乘以_ProjectionParams.x来达到这个目的:

output.pos = float4(2.0 * rasterPosition.x / _ScreenParams.x - 1.0,_ProjectionParams.x * (2.0 * rasterPosition.y / _ScreenParams.y - 1.0),_ProjectionParams.y, // near plane is at -1.0 or at 0.01.0);

这是我们从对象空间到屏幕空间的顶点变换所需要的全部信息。但是,我们仍然需要计算恰当的纹理坐标以便可以在正确的位置查询到纹理贴图。纹理坐标的范围应该在0.01.0之间。它实际上很容易从范围在(-0.5,0.5)之间的在对象空间中的顶点坐标计算出来:

output.tex = float4(input.vertex.x + 0.5, input.vertex.y + 0.5, 0.0, 0.0);// for a cube, vertex.x and vertex.y // are -0.5 or 0.5

用顶点输出参数tex,我们可以使用一个简单的片元程序在纹理贴图中查询颜色,并且用用户自定义的颜色_Color来修改它:

  float4 frag(vertexOutput input) : COLOR{return _Color * tex2D(_MainTex, input.tex.xy);   }

完整着色器代码

如果把所有碎片拼接在一起,我们可以得到以下的着色器,它使用叠加队列在所有对象之后渲染对象,并且使用alpha混合(查看章节“透明度”)以允许透明纹理。它也关闭了深度测试以确保纹理永远不会被遮挡:

Shader "Cg shader for screen overlays" {Properties {_MainTex ("Texture", Rect) = "white" {}_Color ("Color", Color) = (1.0, 1.0, 1.0, 1.0)_X ("X", Float) = 0.0_Y ("Y", Float) = 0.0_Width ("Width", Float) = 128_Height ("Height", Float) = 128}SubShader {Tags { "Queue" = "Overlay" } // 在所有对象之后渲染Pass {Blend SrcAlpha OneMinusSrcAlpha // 使用alpha混合ZTest Always // 关闭深度测试CGPROGRAM#pragma vertex vert  #pragma fragment frag#include "UnityCG.cginc" // defines float4 _ScreenParams with x = width;  // y = height; z = 1 + 1.0/width; w = 1 + 1.0/height// and defines float4 _ProjectionParams // with x = 1 or x = -1 for flipped projection matrix;// y = near clipping plane; z = far clipping plane; and// w = 1 / far clipping plane// User-specified uniformsuniform sampler2D _MainTex;uniform float4 _Color;uniform float _X;uniform float _Y;uniform float _Width;uniform float _Height;struct vertexInput {float4 vertex : POSITION;float4 texcoord : TEXCOORD0;};struct vertexOutput {float4 pos : SV_POSITION;float4 tex : TEXCOORD0;};vertexOutput vert(vertexInput input) {vertexOutput output;float2 rasterPosition = float2(_X + _ScreenParams.x / 2.0 + _Width * (input.vertex.x + 0.5),_Y + _ScreenParams.y / 2.0 + _Height * (input.vertex.y + 0.5));output.pos = float4(2.0 * rasterPosition.x / _ScreenParams.x - 1.0,_ProjectionParams.x * (2.0 * rasterPosition.y / _ScreenParams.y - 1.0),_ProjectionParams.y, // near plane is at -1.0 or at 0.01.0);output.tex = float4(input.vertex.x + 0.5, input.vertex.y + 0.5, 0.0, 0.0);// for a cube, vertex.x and vertex.y // are -0.5 or 0.5return output;}float4 frag(vertexOutput input) : COLOR{return _Color * tex2D(_MainTex, input.tex.xy);   }ENDCG}}
}

当你为一个立方体对象使用这个着色器,纹理贴图会根据摄像机的朝向出现或消失。这是由Unity的裁剪造成的,它不渲染在摄像机(视截体)中可见的完全在场景区域之外的对象。这个裁剪是基于游戏对象的传统变换上的,这跟我们的着色器没有多大关系。为了不激活这个裁剪,我们可以简单地使立方体对象成为摄像机的子对象(通过在Hierarchy Window中把它拖到摄像机下面)。如果立方体对象被放置到摄像机的前面,它会总是保持在相同的相对位置上,这样它就不会被Unity裁剪掉了。(至少在游戏视口中不会了。)

不透明屏幕叠加的改变

着色器的很多变化是可以想象的,比如在叠加前面3D场景的一些对象有不同的混合模式或不同的深度。这里我们只看不透明叠加。

一个不透明的屏幕叠加将会遮挡场景的三角形。如果GPU知道这个遮挡,它就没有必要光栅化这些被遮挡的三角形(比如通过使用延迟渲染或是早期深度测试)。为了确保GPU有机会应用这些优化,我们必须通过设置Tags { "Queue" = "Background" }首先渲染屏幕叠加。而且,我们应该通过移除Blend指令避免混合。随着这些变化,不透明屏幕叠加就有可能提升性能而不是光栅化性能的消耗。

总结

恭喜,你完成了本教程的学习。我们看到了:

  • 如何用Cg着色器渲染屏幕叠加。
  • 如何为不透明屏幕叠加修改这个着色器。

扩展阅读

  • 关于纹理映射,你应该阅读章节“纹理球体”。
  • 关于混合,你应该阅读章节“透明度”。
  • 关于对你空间,屏幕空间,裁剪空间,归一化设置坐标,透视除法等,你应该阅读章节“顶点变换”中标准顶点变换的描述。

这篇关于Cg Programming/Unity/Screen Overlays屏幕叠加的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/797362

相关文章

Weex入门教程之4,获取当前全局环境变量和配置信息(屏幕高度、宽度等)

$getConfig() 获取当前全局环境变量和配置信息。 Returns: config (object): 配置对象;bundleUrl (string): bundle 的 url;debug (boolean): 是否是调试模式;env (object): 环境对象; weexVersion (string): Weex sdk 版本;appName (string): 应用名字;

一款支持同一个屏幕界面同时播放多个视频的视频播放软件

GridPlayer 是一款基于 VLC 的免费开源跨平台多视频同步播放工具,支持在一块屏幕上同时播放多个视频。其主要功能包括: 多视频播放:用户可以在一个窗口中同时播放任意数量的视频,数量仅受硬件性能限制。支持多种格式和流媒体:GridPlayer 支持所有由 VLC 支持的视频格式以及流媒体 URL(如 m3u8 链接)。自定义网格布局:用户可以配置播放器的网格布局,以适应不同的观看需求。硬

Unity Post Process Unity后处理学习日志

Unity Post Process Unity后处理学习日志 在现代游戏开发中,后处理(Post Processing)技术已经成为提升游戏画面质量的关键工具。Unity的后处理栈(Post Processing Stack)是一个强大的插件,它允许开发者为游戏场景添加各种视觉效果,如景深、色彩校正、辉光、模糊等。这些效果不仅能够增强游戏的视觉吸引力,还能帮助传达特定的情感和氛围。 文档

Unity协程搭配队列开发Tips弹窗模块

概述 在Unity游戏开发过程中,提示系统是提升用户体验的重要组成部分。一个设计良好的提示窗口不仅能及时传达信息给玩家,还应当做到不干扰游戏流程。本文将探讨如何使用Unity的协程(Coroutine)配合队列(Queue)数据结构来构建一个高效且可扩展的Tips弹窗模块。 技术模块介绍 1. Unity协程(Coroutines) 协程是Unity中的一种特殊函数类型,允许异步操作的实现

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光 一,前言二,资源包内容三,免费获取资源包 一,前言 在创意的世界里,每一个细节都能决定一个项目的独特魅力。今天,要向大家介绍一款令人惊艳的粒子效果包 ——Super Confetti FX。 二,资源包内容 💥充满活力与动态,是 Super Confetti FX 最显著的标签。它宛如一位

Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(4)

本文仅作笔记学习和分享,不用做任何商业用途 本文包括但不限于unity官方手册,unity唐老狮等教程知识,如有不足还请斧正​​ Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(3)-CSDN博客  这节就是真正的存储数据了   理清一下思路: 1.存储路径并检查 //2进制文件类存储private static string Data_Binary_Pa

Unity Adressables 使用说明(一)概述

使用 Adressables 组织管理 Asset Addressables 包基于 Unity 的 AssetBundles 系统,并提供了一个用户界面来管理您的 AssetBundles。当您使一个资源可寻址(Addressable)时,您可以使用该资源的地址从任何地方加载它。无论资源是在本地应用程序中可用还是存储在远程内容分发网络上,Addressable 系统都会定位并返回该资源。 您

Unity Adressables 使用说明(六)加载(Load) Addressable Assets

【概述】Load Addressable Assets Addressables类提供了加载 Addressable assets 的方法。你可以一次加载一个资源或批量加载资源。为了识别要加载的资源,你需要向加载方法传递一个键或键列表。键可以是以下对象之一: Address:包含你分配给资源的地址的字符串。Label:包含分配给一个或多个资源的标签的字符串。AssetReference Obj

在Unity环境中使用UTF-8编码

为什么要讨论这个问题         为了避免乱码和更好的跨平台         我刚开始开发时是使用VS开发,Unity自身默认使用UTF-8 without BOM格式,但是在Unity中创建一个脚本,使用VS打开,VS自身默认使用GB2312(它应该是对应了你电脑的window版本默认选取了国标编码,或者是因为一些其他的原因)读取脚本,默认是看不到在VS中的编码格式,下面我介绍一种简单快

安卓实现弹出软键盘屏幕自适应调整

今天,我通过尝试诸多方法,最终实现了软键盘弹出屏幕的自适应。      其实,一开始我想通过EditText的事件来实现,后来发现,安卓自带的函数十分强大,只需几行代码,便可实现。实现如下:     在Manifest中设置activity的属性:android:windowSoftInputMode="adjustUnspecified|stateHidden|adjustResi