《Real-Time Rendering 4th Edition》读书笔记--简单粗糙翻译 第二章 渲染管线 The Graphics Rendering Pipeline

写在前面的话:因为英语不好,所以看得慢,所以还不如索性按自己的理解简单粗糙翻译一遍,就当是自己的读书笔记了。不对之处甚多,以后理解深刻了,英语好了再回来修改。相信花在本书上的时间和精力是值得的。

—————————————————————————————————————————————————————————
“链条的坚固程度取决于它最薄弱的环节”

        这章讲的是实时渲染的核心内容,图形渲染管线,通常简称为管线。管线的主要功能是通过给定一个虚拟摄像机一些三维物体光源等来生成或渲染出一个二维图像,因此渲染管线是实时渲染的基础。图2.1描述了渲染管线的过程。物体在图片中呈现出的位置和形状由他们的几何形状、环境特征以及摄像机在环境中的位置决定。物体的外观由材质属性、光源、表面贴图以及渲染方程决定。

图2.1 在左边的图中,位于锥形顶端处是一个虚拟摄像机,只有在摄像机视体里的图元才能被渲染。在透视投影(本例所示)中,视体是一个平截头体。右边的图表示的是摄像机看到的内容。注意左边图中的红色甜甜圈形状的物体并没有被绘制,因为它位于平截头体之外。同理左图中的蓝色扭曲棱体超出平截头体上半部分裁剪掉了。

接下来我们将详细介绍渲染管线的各阶段的功能而不是其实现, 各阶段的应用细节将会在后面章节介绍。

2.1  Architecture 架构

       在现实世界中,管线这个概念被应用到了各行各业,例如工厂的生产线、快餐厨房。在图形渲染管线中,每个阶段都有着大量的工作量。

        渲染管线各阶段是并行执行的,每个阶段又都依赖上一个阶段的结果。理想上,一个非管线系统被分成n个管线阶段可提高n倍性能。能提高性能是使用管线的主要原因。举个例子,一部分人一起准备着大量的三明治,一个人负责面包,一个人负责给面包加肉,剩下的人负责给面包加入浇头。每个人将处理完的三明治交给下一个人进行处理,然后继续处理下一个三明治。如果每个人需要花费20秒处理完手里的一个三明治,那么每个三明治的完成速度最快是20秒,每分钟3个。虽然管线各阶段是并行执行的,但是他们在最慢的阶段完成任务之前会被停止。举例,如果在加肉环节,加入的肉多了,需要花费30秒,那么一分钟最快只能生产2个三明治。对管线而言,加肉阶段就是瓶颈,它决定了整个生产过程的速度。加入浇头阶段(消费者一样)在等待加肉阶段完成之前都处于一个饥饿状态。

        这种结构的管线也应用到了计算机图形实时渲染环境中。一个粗糙的实时渲染管线被分为4个阶段:应用阶段,几何处理阶段,光栅化阶段和像素处理阶段,如图2.2。这个结构是计算机图形实时渲染应用程序的核心,也是后续章节讨论内容的基础。这些阶段本身可以成一个管线,可由几个子阶段组成。我们在这里是按照功能和实现方式区分的。每个阶段都有着明确的工作,但是没有指出工作执行的特定方式。例如一个指定的实现方式是联合两个阶段为一个单元,或者使用可编程核心执行,同时将另外一个更耗时的阶段划分成几个硬件单元。

图2.2 渲染管线的结构粗糙的分成4个阶段:应用阶段(application)、几何处理阶段(geometry processing)、光栅化阶段(rasterization)和像素处理阶段(pixel processing)。每个阶段本身又可以是一个管线,如几何阶段可以分成几个子阶段,绿色矩形所示;或者如像素处理阶段所示,一个阶段(或部分)可以平行处理,粉红色矩形所示。图中,应用程序阶段是一个单一处理过程,但是这个阶段也可以分成多个子阶段或者多个平行阶段。注意光栅化阶段是在判定那些像素在一个图元(例如一个三角形)中。

        渲染速度用每秒多少帧(FPS)来表示,也就是每秒可渲染多少张图。也可以用赫兹(HZ)来表示,赫兹表示每秒更新的频率。当然用时间毫秒来表示也行,也就是渲染一张图所用的时间。生成一张图所用的时间通常不一样,取决于每帧的计算复杂度。FPS不仅可表示特定帧的速率,也可以表示一段时间内的平均速率。Hz通常用于硬件,例如屏幕刷新速率,通常是个固定速率。

        顾名思义,应用阶段由应用程序驱动,因此应用阶段是应用在运行在一些通用CPU上的软件程序中。这些CPU通常是多核的,有多线程并行处理的能力。应用阶段可利用CPU高效处理大量的工作 。这些工作按程序需求可分为传统的碰撞检测、全局加速算法、动画、物理模拟等等。应用阶段下一主要阶段是几何处理阶段,这一阶段主要是处理变换、投影以及其他的各种类型的几何处理。在这阶段主要是计算什么被绘制,如何绘制,绘制在哪。几何处理阶段很明显是应用在图形处理单元(GPU,拥有很多可编程核心)和一些固定操作硬件中。光栅化阶段通常是利用三个顶点作为输入,形成一个三角形,然后找到三角形中所有的像素,然后将这些像素转发到下一阶段。最后,像素处理阶段对每一个像素执行一段程序来决定像素的颜色,是否进行深度测试来判断是否可见,是否和上一个颜色混合出新的颜色。光栅化阶段和像素处理阶段都是整个阶段在GPU中执行。下面四个小节将介绍这四个阶段及其子阶段。更多关于GPU如何处理这些阶段将在第三章详细说明。

2.2 应用阶段

        应用阶段通常在CPU上执行,开发者有完全的自主权。因为,开发者可以完全决定它的实现,并可在后期修改来提高它的性能。这一阶段的改变对后续阶段的性能同样产生影响。例如,在应用阶段的一些算法或者设置可以减少绘制的三角形数。

        一些应用程序的工作可以利用特定的计算着色器在GPU上执行。计算着色器如果忽略其特定的渲染图形功能,可以看作高度并行的通用处理器。

        在应用阶段的最后会将需要渲染的几何图形提交给几何处理阶段。这些几何图形被称为渲染图元,例如点、线和三角形,最后呈现在屏幕上。这也是应用阶段最重要的工作。

        如果应用阶段是基于软件实现的话,那么这一阶段没有像几何处理阶段、光栅化阶段、像素处理阶段那样分成了几个子阶段。为了提高性能,这一阶段通常利用几个处理单元处理工作。在CPU设计上,这通常被称为超标量体系结构,因为他可以同时在工作在几个处理单元上。8.5章节将会介绍一些利用多处理器单元的方法。

        在应用阶段通常会处理碰撞检测。在两个物体之间的碰撞被检测到后,会有对应的处理,例如返回碰撞的物体及碰撞产生的力反馈。在这阶段也处理输入系统,例如键盘、鼠标或头戴式显示器。基于这些输入,不同的处理结果会被执行。还有一些加速算法,例如特定的裁剪算法,一些管线无法处理的工作都会在应用程序阶段处理。

2.3 几何处理阶段

        几何处理阶段的主要工作是负责对大部分三角形和顶点的操作。这阶段可以划分成下图2.3所示的几个功能阶段:顶点着色、投影、裁剪和屏幕映射。

图2.3 几何处理阶段按功能可分成的子阶段

2.3.1 顶点着色

        顶点着色主要有两个工作,计算顶点的位置和计算出程序可能需要的顶点数据,例如法线和纹理坐标。以前,大部分物体着色是计算光作用于顶点位置和法线,并将计算后的颜色存储在顶点。然后这些颜色会插值到三角形。因此,这个可编程的顶点处理单元被称为顶点着色。随着现代GPU的发展,伴随着部分或者全部像素的着色,顶点着色阶段越来越简单,甚至都不需要计算着色方程,这取决于程序的设计者。顶点着色现在越来越方便将数据设置给顶点。例如,顶点着色器可以利用章节4.4和章节4.5中的方法给物体添加动画。

        我们首先描述计算顶点位置。被呈现到屏幕之前,一个模型被变换到好几个空间和坐标系中。一开始,模型处在自己的模型空间,这意味着模型没有进行任何变换。每个模型都可以通过模型变换进行移动和旋转改变坐标位置和朝向,而且可以多次进行变换。在同一场景中每个模型实例可以拥有不同的位置,方向大小而不需要改变模型的基本几何形状。

        模型变换其实对模型的顶点和法线进行变换。一个物体的坐标被称为模型坐标。经过模型变换后,模型可以说处在世界坐标中或者在世界空间中。世界空间是唯一的,每个模型经过各自的模型变换后,都处于世界空间中。

        正如先前提到的,只有在摄像机看到的模型才会被渲染。摄像机在世界空间中拥有一个位置和方向。为了方便投影和裁剪,摄像机和所有的模型都会经过观察变换观察变换的目的是将摄像机转换到原点,并设置好它的朝向,摄像机对准的方向是z轴的反方向,y轴向上,x轴向右。我们约定摄像机是朝向-z轴,有些文中设定的是+z轴。这是语义上的不同,对变换而言,从+z轴到-z轴的变换很简单。经过观察变换后的实际位置和方向取决于底层的应用程序编程接口(API)。观察变换后的空间可以叫摄像机空间,更常见的是叫观察空间或者视野空间。图2.4所示是一个模型经过观察变换后摄像机和模型的变化。模型变换和观察变换都需要用到4x4的变换矩阵,这将在第四章讲到。然而,意识到顶点和法线能被编程计算是很重要的。

图2.4 在左边图中,是一个自顶向下的视图,摄像机在世界空间中的位置不再原点,世界空间的+z轴是朝上的。右图,经过观察变换后,摄像机的位置在原点,并且摄像机的观察方向是-z轴,摄像机的x轴朝右,y轴指向屏幕外。这是为了投影和裁剪更简单更快。图中蓝色区域表示的是视体,这里用的是透视投影,所以视体是平截头体。其他类型的投影都是类似的技术。

        接下来,我们描述顶点着色的第二种输出。为了生成一个足够现实的场景,只是渲染出物体的形状或者顶点是不足够的,物体的外观也是需要模仿的。这包括物体的材质以及光照在物体上的反光。从简单的颜色到细致物理特征,材质和光可以有多种方式建模。

        决定一个光在材质上的效果的操作叫着色。它需要计算物体每个顶点上着色方程。有些顶点计算是在几何处理阶段,还有些是在像素处理阶段。各种材质数据可以存储在顶点,例如顶点坐标,法线,颜色以及任何计算着色方程需要的数字信息。顶点着色生成的数据会被转送到光栅化阶段和像素处理阶段,进行插值,计算表面着色。

        在第三章和第五章,以GPU顶点着色器的形式将会更深入的讨论顶点着色。

       作为顶点着色的组成部分,渲染系统需要执行投影和裁剪。将视体转换到一个极值点为的单位立方体中。体积可以用不同的范围来定义,例如。这个单位立方体被称为标准化视体。首先是在GPU上的顶点着色器中完成投影,投影有两种方式,正交投影(又称平行投影)和透视投影。如图2.5所示,实际上,正交投影只是平行投影中的一种,在建筑学中可见其他的类型的平行投影,例如三向投影、倾斜投影。

图2.5 左边是正交投影 ,右边是透视投影

        注意,投影矩阵,通常会和其他的几何变换组合在一起。

        正交视角的视体通常是一个矩形,经过正交投影后变成一个单位立方体。正交投影的主要特征是平行线经过变换还是平行的。这个变换是由平移变换和缩放变换组合而成。

        透视投影稍微复杂点。在透视投影中,物体里摄像机越远,投影后物体变得越小。此外,平行线会在远方合拢。透视变换模拟了人眼,能感觉到物体大小的变化。在几何学上,视体称为平截头体,就像一个金字塔头部被截断了。这个平截头体同样需要被转换成单位立方体中。不管是正交变换还是透视变换,都可以用一个4x4的矩阵表示,模型经过投影变换后,模型会变换到裁剪坐标中。裁剪坐标是一个齐次坐标,将会在第四章讨论到,这里是没有除以w的。GPU的顶点着色器必须每次都输出的是这种类型的坐标,为了确保下一阶段,裁剪阶段能够工作正常。

        尽管这些矩阵是把一个体积变换成另外一个体积, 但是他们被称为投影是因为在后续的生成的图像中z坐标没有存储,而是存储在了z-buffer中。通过这种方式,模型就从三维空间投影到了二维平面中。

2.3.2  可选的顶点处理

        每个管线都有刚描述到的顶点处理。一旦该顶点处理完成,GPU有几个可选阶段,按照顺序:细分曲面(tessellation),几何着色( geometry shading),流输出( stream output)。他们需要看硬件是不是支持,并不是所有的GPU都支持,以及看程序是否需要。他们彼此之间相互独立,通常没用到。第三章会讲述更多细节。

        第一个可选阶段是细分曲面。想象一下,你有一个弹跳球对象。如果你只是用一些列的三角形来描述它,那么你会面临一个质量和性能问题。你的球可能在5米外看起来很好,但是仔细观察每个三角形,特别是轮廓处的三角形,是看得见的。但是一旦你为了提高质量,用更多地三角形来描述球,你可能在离球足够远且球在屏幕上只有几个像素的时候浪费掉大量的处理时间和内存。而细分曲面,可以用适当的三角形表示一个曲面。

        我们已经谈了一点三角形,但是到目前为止我们只是处理了顶点。顶点可以用来表示点、线、三角形或其他的物体。顶点也可以用来表示曲面,例如球。这些表面需要一组控制点(称为Patch,可以理解为一个很粗糙的曲面)来定义,而每个控制点又由一组顶点构成。细分曲面阶段可分成几个子阶段——外壳着色器,细分曲面,域着色器——将这些控制点顶点转换成更大的顶点集合,然后用这个顶点集合制作出更多地新的三角形。场景中的摄像机用来决定多少三角形需要被制作,当摄像机靠近控制点Patch的时候需要更多地三角形,反之需要的三角形少。

        下一个可选阶段是几何着色器。几何着色器的出现比细分曲面着色器早,在GPU上几何着色器要被支持的更多。类似于细分曲面着色器,几何着色器也是把各图元的顶点细分归类,然后生成更多地顶点。这个阶段相比细分曲面要简单的多,因为生成的顶点是有数量限制的,输出的图元种类也有更多地限制。几何着色器有很多的用途,其中最为流行的用法是来生成粒子,比如模拟烟花爆竹的效果。每个火球都可以由一个点,单个顶点来表示。几何着色器可以把这些点转变成一个面朝着观察者的拥有些许像素的正方形以方便进行着色。

        最后一个可选阶段是流输出。这个阶段我们可以把GPU当做一个几何引擎。没有将我们处理好的顶底输出给下一个管线阶段进而渲染到屏幕,而是选择输出这些顶点到一个数组中以便将来用得着。这些数据可以存储起来,以便被CPU或GPU在未来使用。流输出最典型的使用是用来模拟粒子,例如烟花。

        可选的三个阶段的顺序是——细分曲面,几何着色,流输出,每一个都是独立可选的。不管这几个阶段是否被选,渲染管线会继续走到下一个阶段,需要判定生成顶点(坐标是齐次坐标)是否在摄像机视野内。

2.3.3 裁剪

        只有全部或者部分在视体内的图元才能被传给光栅化阶段,然后被绘制在屏幕上。如果图元完全在视体之外,并不会传给光栅化,因此不会被渲染。对于那些部分在视体内的图元,需要进行裁剪掉在视体之外的部分。这样,在视体之外的顶点会被视体和连线相交处的新顶点取代。经过投影矩阵后,意味着图元是用单位立方体进行裁剪。在裁剪之前执行观察变换和投影变换的好处是可以简化裁剪,图元总是用单位立方体进行裁剪。

        裁剪过程如图2.6所示。除了视体的6个裁剪面外,还可以自定义额外的裁剪面,818页处的图19.1展示了一种自定义裁剪,称为切割(sectioning)。

        裁剪每步使用的是投影后生成的4值齐次坐标。在透视空间内并不会对三角形进行线性插值。齐次坐标的第4个值在透视投影的时候很重要。最后通过透视除法将三角形的坐标结果转换到三维规格化设备坐标中。前面提到的,视体的范围变成了从几何阶段的最后一个步骤会将裁剪空间变换到屏幕空间。

图2.6  投影变换之后,只有在单位立方体内的图元才会进行下一步处理。因此,在单位立方体之外的图元是会被剔除的,在单位立方体之内的图元被保留。和单位立方体相交的图元,相交的交点会变成新的顶点,立方体之外的顶点会被丢弃。

2.3.4 屏幕映射

在视体内的图元被传输到屏幕映射阶段,此时的坐标仍然是三维坐标。每个图元的x轴和y轴被变换为屏幕坐标屏幕坐标加上z轴被称为窗口坐标。假设屏幕需要被渲染到窗口中,窗口的左下角坐标为,右上角的坐标为,有屏幕映射就是一个缩放的操作。映射后的x,y坐标称为屏幕坐标,z轴(opengl范围是[-1,1], directx的范围是[0,1])同样被映射到[z1,z2]范围内,其中z1=0,z2=1是默认值。可以通过API来改变这两个值。z轴重映射后的屏幕坐标被传输到光栅化阶段。图2.7表示了屏幕映射过程。

图2.7 投影变换后在单位立方体内的图元经过屏幕映射后在屏幕上找到对应的坐标位置。

        接下来描述如何通过整数和浮点数关联到像素点。在笛卡尔坐标系中,给定一组水平像素[0,9],最左边像素的最边缘的浮点值坐标为0.0,像素的中心坐标为0.5。所以一组像素[0,9]包含区间为[0,10),公式如下

d表示像素的索引,是离散的整数,c表示像素点在内的连续浮点数。

        所有的API关于像素位置的坐标都是从左往右递增的,至于零点坐标是在左上角还是左下角是不一致的,OpenGL用的是笛卡尔坐标,左下角是零点坐标,而DirectX大部分时候是左上角作为零点的,需要视情况而定。当从一种API移植到另外一种API,考虑到两者之间的不同很重要。

2.4 光栅化阶段

        有了经过变换和投影后的顶点及几何处理后的相关渲染数据,接下来一个阶段的目标就是找到所有需要被渲染的图元的全部像素,我们称这个过程为光栅化,它可分成两个子阶段:设置三角形和遍历三角形,如图2.8所示。注意这些处理也可以处理点和线,之所以子阶段名字里有三角形是因为三角形最为常见。光栅化又叫扫描转换,是将屏幕空间里的二维顶点(包含有z值表示深度和顶点相关的各种渲染信息)转换到屏幕像素点的过程。光栅化可以认为是几何处理和像素处理之间的一个同步点,因为这里构成三角形的顶点由几何处理得到,同时光栅化后的像素会交给像素处理阶段进行处理。

图2.8 左边:光栅化分成两个子阶段,设置三角形,遍历三角形。 右边:像素处理阶段分成两个阶段,像素着色,合并。

        判断三角形是否覆盖住了像素取决于你如何设置GPU管线。举个例子,你可能使用点采样去决定是否在三角形里面 。最简单的例子是用一个点采样像素的中心,如果中心点在三角形内,可以认为该像素在三角形内。你也可以利用多个点来采样,例如超级采样supersample或多重采样multisample抗锯齿技术。还有一种方式是利用保守光栅化技术,如果像素只要有一点和三角形重叠,就认为像素在三角形内。

2.4.1 设置三角形

        在这个阶段会计算三角形的差异,边缘方程等数据。这些数据会被用来遍历三角形,插值几何阶段生成的各种着色数据。这个由硬件支持。

2.4.2 遍历三角形

        这里是检测每个像素的中心(或采样点)是否在三角形内,并给在三角形内的像素生成一个片元(fragment)。在5.4章节有更详细的采样方法。

       寻找哪些像素或者采样点在三角形内的过程叫遍历三角形。每个三角形片元的属性内容都由三角形三个顶点插值而来。这些属性包括片元的深度,几何阶段生成的着色数据。每个图元内的像素或采样点都会传送给像素处理阶段,接下来会讲到。

2.5 像素处理阶段

        到目前为止,由前面一系列步骤生成的像素的都拿到了。像素处理阶段可分成像素着色和合成两个阶段。如图2.8所示。像素处理阶段可以认为是对每个图元内每个像素或采样点进行逐像素或逐采样点计算和操作的地方。

2.5.1 像素着色

        任何逐像素着色计算都在这里进行,以插值的着色数据作为输入。输出到下一阶段的是一个或者多个颜色值。不像设置三角形和遍历三角形是由专门的硬件来计算,像素着色阶段是由可编程的GPU单元执行。程序员提供一段程序给像素着色器(或者片元着色器)。这里需要用到大量的技术,其中最重要的一项是纹理(texturing)。第六章会详细讲到texturing。简单说,texturing一个物体就是为了各种各样的目的给一张或多张图像添加到物体上。图2.9展示的是一个简单的例子。 这些图像可以是一维、二维、三维图像,其中二维图像运用最多。最简单的理解是经过texturing后会生成每一个片元的颜色值,并会传送到下一阶段。

图2.9 左上图是一个没有贴图的巨龙模型  左下图是添加了各种贴图后的巨龙模型

2.5.2 合并

        每个像素的信息都存储在颜色缓冲(color buffer)中,颜色缓冲是一个颜色值得矩阵(包含红绿蓝三色通道)。合并阶段的职责是将由像素着色阶段生成的颜色值和当前存储在颜色缓冲中的颜色进行结合。这个阶段又可以称为ROPraster operations (pipeline)或 render output unit,取决于你问谁)。不像像素着色阶段,GPU子单元处理这个阶段不是完全可编程的,然而他是可以高度可配的,可生成各种效果。

        这个阶段同时也负责解决可见性。这意味着当整个场景被渲染后,颜色缓冲应该包含有所有摄像机能看到的图元的颜色值。对大部分甚至全部图形硬件,都是通过z-buffer(又称深度缓冲)来做这件事的。深度缓冲和颜色缓冲类似,对每个像素而言,他将z值存储到了离得最近的图元上。这意味着当一个图元上的某个像素需要被渲染的时候,需要计算该图元上该像素的z值并同深度缓冲中当前像素的z值进行比较。如果新计算出来的z值比存储在深度缓冲中的z值小,则说明当前像素点上的正在渲染的图元比先前离摄像机最近的图元更接近摄像机。因此,当前像素的z值和颜色值需要更新。如果计算出来的z值比存在深度缓冲中的z值大,则颜色缓冲和深度缓冲中当前像素点的颜色和z值是不需要改变的。深度缓冲算法是简单的,复杂度为O(n),其中n是需要渲染的图元的数量,适用于任何能给相关像素计算z值得图元。同时注意这个算法运行大部分图元是无序渲染的,这也是该算法流行的另外一个原因。然而,因为深度缓冲只存储了屏幕上每个点的一个单一值,所以它并不能给半透明图元用。半透明图元必须在所有不透明图元渲染之后进行渲染, 并且是从后到前的顺序进行,或者使用一个独立与顺序无关的算法(5.5节有讲)。透明度是基本z-buffer最主要的几个缺点之一。

        我们已经说过了颜色缓冲是用来存储每个像素的颜色值,深度缓冲存储的是每个像素的z值。然后,还有其他的通道或者缓冲可以用来过滤或抓取片元信息。和颜色缓冲相关的alpha通道被用来存储像素的透明度。在旧的API中,alpha通道被用来进行alpha测试选择性的剔除一些像素。如今,剔除操作可以在像素着色器中完成,并且任何类型的计算都可以触发剔除。alpha测试可以被用来确保全透明片元不会影响到深度缓冲。

        模板缓冲是一种离屏缓冲,被用来记录已渲染的图元的位置。通常每个像素有8bits。图片可以通过各种功能被渲染进模板缓冲,然后模板缓冲被用来控制是否渲染进颜色缓冲和深度缓冲。举例,假设一个圆形区域绘制进模板缓冲中,就可以利用模板缓冲只在圆形区域的图元能被渲染进颜色缓冲中。模板缓冲能用来做一些特殊效果。组合阶段最后的操作被称为光栅操作(raster operations ,ROP)或者 混合操作(blend operation)。它可以将三角形内正在处理的像素颜色同颜色缓冲中的颜色进行混合。这可以影响透明度或者颜色样本的卷积。混合不是完全可编程的,而是可配的。然而一些API已经支持光栅顺序视图( raster order views),或者称为像素着色器可序( pixel shader ordering),使得混合可编程。

        帧缓冲通常包含系统中所有的缓冲。

        一旦图元到达并通过了光栅化阶段,那么这些图元将被绘制到屏幕上。颜色缓冲中的内容将会绘制到屏幕上。为了避免,观察者看到图元正在被光栅化的过程,产生了双缓冲技术。这意味着场景会被离屏渲染进后缓冲中,一旦场景被渲染完成,后缓冲会被交换到前缓冲,然后呈现到屏幕上。这个交换发生在垂直回扫(vertical retrace)中(垂直同步),这是为了安全 (防止出现闪烁断层等现象)。

发布了7 篇原创文章 · 获赞 2 · 访问量 1198

猜你喜欢

转载自blog.csdn.net/xyxsuoer/article/details/103732425
今日推荐