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

相关文章

笔记本电脑屏幕模糊?6招恢复屏幕清晰!

在数字化时代的浪潮中,笔记本电脑已成为我们生活、学习和工作中不可或缺的一部分。然而,当那曾经清晰明亮的屏幕逐渐变得模糊不清时,无疑给我们的使用体验蒙上了一层阴影。屏幕模糊不仅影响视觉舒适度,更可能对我们的工作效率和眼睛健康构成威胁。 遇到笔记本电脑屏幕模糊的情况时我们应该如何解决?本文将与大家分享6个简单易懂的解决方法。 方法一:调整Windows分辨率 电脑屏幕模糊显示不清晰怎

【Unity Shader】片段着色器(Fragment Shader)的概念及其使用方法

在Unity和图形编程中,片段着色器(Fragment Shader)是渲染管线中的一个阶段,负责计算屏幕上每个像素(片段)的颜色和特性。片段着色器通常在顶点着色器和任何几何处理之后运行,是决定最终像素颜色的关键步骤。 Fragment Shader的概念: 像素处理:片段着色器处理经过顶点着色器和几何着色器处理后,映射到屏幕空间的像素。颜色计算:它计算每个像素的颜色值,这可能包括纹理采样、光

【Unity Shader】Alpha Blend(Alpha混合)的概念及其使用示例

在Unity和图形编程中,Alpha Blend(也称为Alpha混合)是一种用于处理像素透明度的技术。它允许像素与背景像素融合,从而实现透明或半透明的效果。Alpha Blend在渲染具有透明度的物体(如窗户、玻璃、水、雾等)时非常重要。 Alpha Blend的概念: Alpha值:Alpha值是一个介于0(完全透明)和1(完全不透明)的数值,用于表示像素的透明度。混合模式:Alpha B

Apple - Media Playback Programming Guide

本文翻译整理自:Media Playback Programming Guide(Updated: 2018-01-16 https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/MediaPlaybackGuide/Contents/Resources/en.lproj/Introduction

Unity Meta Quest 开发:关闭 MR 应用的安全边界

社区链接: SpatialXR社区:完整课程、项目下载、项目孵化宣发、答疑、投融资、专属圈子 📕教程说明 这期教程我将介绍如何在应用中关闭 Quest 系统的安全边界。 视频讲解: https://www.bilibili.com/video/BV1Gm42157Zi 在 Unity 中导入 Meta XR SDK,进行环境配置后,打开 Assets > Plugins > An

Unity 字体创建时候容易导致字体文件不正确的一种情况

上面得到了两种字体格式,一种是TextMeshPro的,另一种是Unity UI系统中默认使用的字体资源。其原因是创建的位置不同导致的。 1.下面是TextMeshPro字体创建的位置 2:下面是Unity UI系统中默认使用的字体资源

iOS中截取屏幕中局部图片

-(void)screenShotRect:(CGRect)aRect //arect 想要截图的区域{UIGraphicsBeginImageContextWithOptions(CGSizeMake(640, 960), YES, 0);[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];UIImage *viewI

摄像头画面显示于unity场景

🐾 个人主页 🐾 🪧阿松爱睡觉,横竖醒不来 🏅你可以不屠龙,但不能不磨剑🗡 目录 一、前言二、UI画面三、显示于场景四、结语 一、前言 由于标题限制,这篇文章主要是讲在unity中调用摄像头,然后将摄像头捕捉到的画面显示到场景中,无论是UI画面还是场景中的某个物体上;至于应用的场景可以用于AR增强现实。 那么话不多说,直接开始今

unity开发 --------- NGUI (UITable)

unity开发 --------- NGUI UITable与UIGrid相似,都是实现自动排序的。但UIGrid的元素大小是由我们来指定的,而Table中的元素的大小是根据元素本身计算出来的。 UITable还保存了元素的顺序List<Transform>。每次重排序,都会更新此List。除了要计算元素的Bound和保存List外,其他基本与UIGrid一致。 unity开发 ---

unity开发 --------- NGUI (UIGrid)

unity开发 --------- NGUI  UIGrid可以实现多个gameobject自动排序。可以设定其排序方向、每个元素的宽度,高度等。 public Arrangement arrangement = Arrangement.Horizontal;public int maxPerLine = 0;public float cellWi