
技术美术常见面试题整理
通过将几何和材质信息存储在G-Buffer中,可以实现多个光源的逐像素光照计算、后期处理效果和渲染技术,如SSAO(屏幕空间环境光遮蔽)、SSR(屏幕空间反射)等。通常,MSAA适用于硬件支持的场景,FXAA和SMAA适用于后处理效果,而TXAA则适用于高质量图像的需求,但需要更高的计算性能。它是在OpenGL中进行高效渲染的重要工具之一,特别适用于复杂几何体的绘制和动态几何变化的场景。(Occl
渲染部分
一、什么是渲染管道
渲染管道(Rendering Pipeline)是指在计算机图形学中用于生成最终图像的一系列步骤和过程。渲染管道负责将场景中的三维模型、材质、光照等信息转换成最终的二维图像,以便显示在屏幕上。在游戏引擎和图形应用程序中,渲染管道是实现图形渲染的核心组成部分之一。
渲染管道通常由多个阶段组成,每个阶段负责处理不同的图形渲染任务,例如几何处理、光照计算、阴影投射、透明度处理等。常见的渲染管道包括传统的固定渲染管道(Fixed Function Pipeline)和基于编程的可编程渲染管道(Programmable Pipeline)。
传统的固定渲染管道是一种较早期的渲染技术,它包含了固定的阶段和固定的功能,例如顶点变换、光照计算、纹理映射等。开发者无法修改或定制渲染管道的内部行为,只能通过调整参数来控制渲染效果。
而基于编程的可编程渲染管道则提供了更大的灵活性和定制性。它允许开发者编写自定义的着色器程序,控制渲染管道的每个阶段的行为,并根据需求实现各种复杂的图形效果和技术。
渲染管道的设计对于图形性能、渲染效果和开发者的工作流程都具有重要影响。因此,选择合适的渲染管道并进行优化是实现高质量图形渲染的关键之一。在现代图形应用程序和游戏引擎中,通常会使用基于编程的可编程渲染管道,以满足对图形效果和性能的高要求。
二、Material 和 SharedMaterial的区别
在 Unity 中,Mesh Renderer 组件用于在场景中渲染 3D 模型的外观。在 Mesh Renderer 中有两个与材质相关的属性:Material 和 SharedMaterial,它们之间有一些区别:
-
Material:
- Material 属性是 Mesh Renderer 的实例属性,用于指定单个物体的材质。
- 当你在 Inspector 窗口中直接将材质拖拽到 Mesh Renderer 组件上时,你实际上是在为该物体分配一个新的材质,并且该材质只会影响到当前的这个物体。
- 如果你更改了 Material 属性所引用的材质的属性,只有当前物体会受到影响,其他使用相同材质的物体不会发生变化。
-
SharedMaterial:
- SharedMaterial 属性是 Mesh Renderer 的共享属性,它用于指定多个物体共享的材质。
- 当你在 Inspector 窗口中直接将材质拖拽到 Mesh Renderer 组件上,并选择 Apply 或 Apply All,你实际上是在为该物体分配一个共享材质,这个材质将会影响到所有共享相同材质的物体。
- 如果你更改了 SharedMaterial 属性所引用的材质的属性,所有共享该材质的物体都会受到影响。
总的来说,Material 和 SharedMaterial 的区别在于对材质的影响范围。Material 用于指定单个物体的材质,而 SharedMaterial 用于指定多个物体共享的材质。因此,如果你希望多个物体共享同一个材质,并且对该材质的更改要同时反映在所有物体上,可以使用 SharedMaterial。如果你希望一个物体具有自己独特的材质,并且更改该材质只影响到当前物体,那么可以使用 Material。
三、GPU的工作原理
GPU(Graphics Processing Unit,图形处理单元)是计算机中用于处理图形相关任务的特定硬件。其工作原理可以简单概括为以下几个步骤:
-
几何处理:
- 首先,GPU 接收来自 CPU 的图形数据,如顶点、多边形等。
- GPU 的几何处理单元会对这些数据进行处理,进行顶点转换(如模型到世界空间、世界到相机空间的转换)、裁剪(将超出屏幕范围的部分裁剪掉)等操作。
-
光栅化:
- 经过几何处理后,图形数据会被光栅化成像素点,即将图形转换成屏幕上的像素。
- 光栅化过程中,GPU 会根据三角形的形状和位置在屏幕上填充像素。
-
像素处理:
- 在像素处理阶段,GPU 对每个像素进行处理,包括应用纹理、光照、阴影等效果。
- GPU 会执行片段着色器(Fragment Shader)来计算每个像素的最终颜色。
-
输出到屏幕:
- 处理完成后,GPU 将处理好的图像数据输出到屏幕上显示。
需要注意的是,现代的 GPU 在处理图形任务时采用了并行计算的方式,通过大量的并行处理单元同时处理多个像素,以提高图形处理的效率和性能。此外,GPU 还具有专用的显存(Video RAM,VRAM)用于存储图形数据,以提供更快的访问速度和更大的存储容量。
总的来说,GPU 的工作原理是将图形数据经过几何处理、光栅化、像素处理等阶段,最终输出到屏幕上显示,同时利用并行计算的方式提高图形处理的效率和性能。
四、常见几种阴影的判断方法及其原理
常见的几种阴影判断方法包括:
-
Shadow Mapping(阴影映射):
- 原理:阴影映射是一种基于深度缓冲区的技术。首先,从光源的视角渲染场景,并将深度信息存储在深度缓冲区中。然后,从相机的视角渲染场景,并通过采样深度缓冲区来判断光线是否被遮挡,从而确定阴影的位置。
-
Shadow Volume(阴影体积):
- 原理:阴影体积技术通过创建投射阴影的几何体积来确定阴影的位置。这些几何体积通常是由光源到阴影物体的边界和阴影多边形构成的,然后使用体积剔除算法(如Stencil Buffer)来确定投射阴影的区域。
-
Ray Traced Shadows(光线追踪阴影):
- 原理:光线追踪阴影是一种基于光线追踪的技术,通过跟踪光线并检测光线与阴影物体之间的交点来确定阴影的位置。这种方法可以产生非常真实的阴影效果,但通常需要大量的计算资源。
-
Volume Shadows(体积阴影):
- 原理:体积阴影技术通过模拟光线在物体内部产生的阴影效果来确定阴影的位置。它通常使用体积渲染技术来计算每个像素或片段的体积光照强度,从而产生阴影效果。
这些阴影判断方法都有各自的优缺点和适用场景。在实际应用中,通常会根据场景需求、性能要求和硬件支持选择合适的阴影技术来实现阴影效果。
五、五、Mipmap 是什么,优点和缺点
Mipmap是一种纹理优化技术,它通过在原始纹理图像的基础上生成一系列较小分辨率的图像,以适应不同的距离和缩放级别。这些较小分辨率的图像被预先计算并存储在纹理中,以供在纹理采样过程中使用。
优点:
-
纹理过滤:Mipmap可以提供更平滑的纹理过滤效果,避免了在纹理放大或缩小时出现锯齿和失真等问题。
-
渲染性能优化:使用Mipmap可以减少纹理采样时对内存带宽的需求,从而提高了渲染性能。由于Mipmap包含了一系列不同分辨率的图像,系统可以根据纹理的距离和缩放级别选择合适的Mipmap级别进行采样,避免了不必要的纹理数据加载和内存带宽消耗。
-
纹理映射细节:Mipmap可以在远距离观察时选择低分辨率的Mipmap级别,以节省内存和提高渲染性能。
-
防止纹理闪烁:使用Mipmap可以减少纹理采样时的镜像或重复纹理闪烁现象。
缺点:
-
内存消耗:存储Mipmap级别需要额外的内存空间,尤其是对于大型纹理而言,会增加内存开销。
-
加载时间:生成和加载Mipmap级别需要额外的计算和存储时间,可能会导致加载时间增加。
-
纹理失真:Mipmap可能会引入一些纹理失真或模糊,特别是在特定情况下(如纹理边缘处)可能会出现不理想的效果。
综上所述,Mipmap是一种用于优化纹理渲染性能和质量的技术,它提供了许多优点,如纹理过滤、渲染性能优化和防止纹理闪烁等,但也存在一些缺点,如额外的内存消耗、加载时间增加和可能引入的纹理失真等。在实际应用中,需要权衡其优缺点,根据具体需求选择是否使用Mipmap。
六、Shader有几种类型,有什么区别
在 Unity 中,Shader 主要分为 Surface Shader、Vertex and Fragment Shader 和 Compute Shader 这三种主要类型。
-
Surface Shader:
- Surface Shader 是一种高级的着色器编写方式,它封装了大部分渲染管线的细节,使得编写表面着色器更加简单和直观。
- Surface Shader 主要用于实现材质的外观效果,例如漫反射、高光、法线贴图等。
- Surface Shader 使用 HLSL 和 Cg 语言编写,并通过 Unity 的表面着色器语言来定义。
-
Vertex and Fragment Shader:
- Vertex Shader 和 Fragment Shader 是传统的着色器编写方式,它们需要手动编写顶点和片段着色器代码。
- Vertex Shader 用于处理顶点数据,例如顶点变换、法线变换等。
- Fragment Shader 用于处理片段数据,例如颜色计算、光照计算等。
- 使用 Vertex and Fragment Shader 编写的着色器可以更加灵活地控制渲染过程,适用于需要定制化渲染效果的场景。
-
Compute Shader:
- Compute Shader 是一种用于通用计算的着色器类型,它可以在 GPU 上进行大规模并行计算。
- Compute Shader 可以用于实现各种复杂的计算任务,例如物理模拟、图像处理、粒子系统等。
- 使用 Compute Shader 可以充分利用 GPU 的并行计算能力,提高计算效率和性能。
这三种类型的 Shader 在功能和使用方式上有所不同:
- Surface Shader 主要用于实现材质的外观效果,封装了渲染管线的细节,更适合于简单的表面效果。
- Vertex and Fragment Shader 提供了更高的灵活性,可以实现更复杂的渲染效果,适用于需要定制化渲染效果的场景。
- Compute Shader 则用于通用计算,适用于各种复杂的计算任务,可以充分利用 GPU 的并行计算能力。
七、 渲染队列有哪几种
在 Unity 中,渲染队列(Rendering Queue)用于确定渲染对象在渲染时的优先级和顺序。渲染队列的数值越小,渲染优先级越高,会先被渲染。常见的渲染队列有以下几种:
-
Background(后景):用于渲染天空盒和其他背景元素。通常使用在天空盒材质中。对应数值为:1000。
-
Geometry(几何体):用于渲染大多数普通物体的默认渲染队列。包括大部分的3D物体、地形等。对应数值为:2000。
-
AlphaTest(Alpha 测试):用于需要进行 alpha 测试的物体。适用于有透明度但不需要透明排序的物体。对应数值为:2450。
-
Transparent(透明):用于透明物体的渲染,这些物体需要按照深度进行排序渲染。对应数值为:3000。
-
Overlay(覆盖):用于渲染UI元素等在最顶层显示的对象。对应数值为:4000。
这些渲染队列决定了 Unity 渲染引擎中对象的渲染顺序和优先级,对于混合和透明度等效果具有重要作用。通常情况下,我们可以通过为材质指定不同的渲染队列数值来控制渲染顺序,以达到特定的渲染效果。
八、Skinned Mesh 的实现原理
Skinned Mesh 的实现原理涉及到骨骼动画技术,是用于模拟角色、生物等动态形变的一种技术。下面是 Skinned Mesh 的简要实现原理:
-
骨骼系统:
- Skinned Mesh 的实现基于骨骼系统。骨骼系统由一系列骨骼(bones)组成,每个骨骼都有一个局部空间的变换(旋转、缩放、平移),并且它们可以形成一个层级结构。
- 每个骨骼都与一个或多个顶点相关联。这些顶点通过权重(weight)来表示与骨骼的连接强度。
-
绑定网格:
- 在 Skinned Mesh 的实现中,一个网格(mesh)会与骨骼系统进行绑定。这个网格包含了一系列顶点,它们是模型表面的点。
- 每个顶点都有一组关联的骨骼以及对应的权重。这些权重表示了每个骨骼对顶点的影响程度。
-
顶点变换:
- 在动画播放时,骨骼系统中的骨骼会发生变换,例如旋转、缩放、平移等。这些变换会影响到与之关联的顶点。
- Skinned Mesh 在运行时会根据骨骼的变换信息,计算每个顶点的最终位置。这个过程被称为顶点变换(Vertex Skinning)。
-
顶点蒙皮:
- 顶点蒙皮是 Skinned Mesh 中的核心技术。它通过组合与骨骼关联的变换来计算每个顶点的最终位置。通常,这个过程使用矩阵运算来实现,将每个顶点的初始位置乘以与之关联的骨骼的变换矩阵,并根据权重进行加权求和。
-
渲染:
- 一旦顶点的最终位置计算完成,Skinned Mesh 就可以将这些顶点渲染到屏幕上。通常,这些顶点会构成三角形,并且可以通过着色器对其进行光照和材质处理。
综上所述,Skinned Mesh 的实现原理基于骨骼系统和顶点变换技术,它通过计算与骨骼关联的顶点的最终位置来实现模型的动态形变,从而实现角色、生物等的动画效果。
九、什么是DrawCall,如何降低DrawCall
DrawCall 是指 CPU 向 GPU 发送绘制请求的操作,它是游戏引擎中用来绘制场景中的每个物体的一次绘制调用。每个 DrawCall 都需要一定的 CPU 时间来准备和发送绘制指令,因此在游戏中会尽量减少 DrawCall 的数量,以提高游戏的性能。
降低 DrawCall 的数量可以通过以下几种方式来实现:
-
合批和批处理:将多个物体的绘制请求合并成一个大的绘制请求,减少 DrawCall 的数量。例如,将多个相同材质的物体合并成一个网格对象,或者使用合批工具自动合并小的网格对象。
-
纹理图集:将多个小纹理合并成一个大的纹理图集,减少 DrawCall 的数量。这样可以减少切换纹理的开销,提高绘制效率。
-
静态批处理:将静态物体合并成一个大的网格对象,以减少 DrawCall 的数量。静态批处理适用于不需要运行时修改的物体,可以显著减少绘制调用的数量。
-
GPU Instancing:利用 GPU 实例化技术来绘制大量相似的物体,只需发送一次绘制指令,就可以绘制多个实例。这样可以大幅减少 DrawCall 的数量,并提高绘制效率。
-
减少透明物体:透明物体的绘制需要进行深度排序和混合操作,会增加 DrawCall 的数量和渲染开销。因此,尽量减少透明物体的数量,或者将它们合并成一个对象,可以降低 DrawCall 的数量。
-
优化 UI 绘制:UI 组件的绘制也会产生 DrawCall,尤其是复杂的 UI 布局。可以通过合并 UI 元素、减少层次深度、使用图集等方式来降低 UI 的 DrawCall 数量。
综上所述,通过合批、纹理图集、静态批处理、GPU Instancing、减少透明物体和优化 UI 绘制等方式,可以有效地降低 DrawCall 的数量,提高游戏的性能和渲染效率。
十、前向渲染和延迟渲染的区别
前向渲染(Forward Rendering)和延迟渲染(Deferred Rendering)是两种常见的渲染技术,它们在渲染管线中的实现方式和特点有所不同,主要区别如下:
-
渲染方式:
- 前向渲染:前向渲染是一种传统的渲染技术,它按顺序渲染场景中的每个物体,从而生成最终的图像。在每个像素上,都需要执行一次完整的光照计算,包括光照、阴影、反射等,然后将结果合成到最终的图像中。
- 延迟渲染:延迟渲染是一种先渲染几何信息到几何缓冲区,然后在后处理阶段再计算光照的渲染技术。在延迟渲染中,渲染管线首先将几何信息(如位置、法线、颜色等)存储到几何缓冲区(G-buffer)中,然后在后处理阶段,根据存储的几何信息进行光照计算和渲染。
-
性能表现:
- 前向渲染:前向渲染适用于场景中物体数量较少、光源数量较少的情况,它可以直接利用硬件的深度缓冲和模板缓冲来进行深度测试和遮挡剔除,性能表现较为稳定。
- 延迟渲染:延迟渲染适用于场景中物体数量较多、光源数量较多的情况,它可以有效地减少光照计算的开销,提高渲染性能。但是延迟渲染也存在一些缺点,例如对透明物体的渲染支持不够好、需要额外的内存开销等。
-
渲染效果:
- 前向渲染:前向渲染可以实现高质量的光照效果,包括实时阴影、透明物体的正确渲染等。但是在处理大量光源时性能会受到影响。
- 延迟渲染:延迟渲染的主要优势在于可以处理大量的光源,而且渲染效果较为稳定。但是延迟渲染也存在一些限制,例如不太适合处理透明物体、对于复杂的光照效果需要额外的后处理步骤等。
综上所述,前向渲染和延迟渲染各有优势和局限性,选择合适的渲染技术取决于具体的场景需求和性能要求。
以下是一个简单的伪代码示例,说明前向渲染和延迟渲染的区别:
前向渲染:
for each object in scene:
for each pixel in object:
// 执行光照计算
color = CalculateLighting(object, pixel)
// 在屏幕上绘制像素
DrawPixel(pixel, color)
延迟渲染:
// 准备几何缓冲区
PrepareGeometryBuffer()
// 光照计算阶段
for each light in scene:
// 渲染到光照缓冲区
RenderLight(light)
// 合成阶段
for each pixel in screen:
// 从几何缓冲区获取像素信息
geometryData = ReadGeometryBuffer(pixel)
// 从光照缓冲区获取像素的光照信息
lightingData = ReadLightingBuffer(pixel)
// 根据像素信息和光照信息合成最终像素颜色
finalColor = Combine(geometryData, lightingData)
// 在屏幕上绘制像素
DrawPixel(pixel, finalColor)
前向渲染和延迟渲染的主要区别在于渲染过程的顺序和步骤:
- 前向渲染:对于场景中的每个物体和每个像素,都需要执行完整的光照计算过程,并直接在屏幕上绘制像素。
- 延迟渲染:首先将几何信息渲染到几何缓冲区,然后针对每个光源分别进行光照计算,并将结果渲染到光照缓冲区。最后,在合成阶段,从几何缓冲区和光照缓冲区中读取像素信息和光照信息,并根据这些信息合成最终的像素颜色。
延迟渲染的主要优势在于可以处理大量的光源和复杂的光照效果,而且在处理透明物体和阴影等方面也比较灵活。然而,延迟渲染也存在一些缺点,例如对于大规模透明物体的渲染支持不够好,对于移动设备等资源有限的环境可能会存在性能问题。
十一、为什么延迟渲染对于透明物体支持不够好
延迟渲染在处理透明物体时存在一些挑战,主要是由于透明物体的渲染方式与不透明物体有所不同,导致在延迟渲染中的处理方式相对复杂。以下是一些导致大规模透明物体渲染支持不够好的主要原因:
-
渲染顺序问题:延迟渲染通常将几何信息渲染到几何缓冲区中,然后根据光照信息在合成阶段进行像素合成。然而,透明物体需要按照深度顺序进行渲染,即先渲染远处的物体再渲染近处的物体,以保证正确的透明效果。这种渲染顺序的要求与延迟渲染的工作方式不太相符,需要额外的处理来实现正确的透明效果。
-
深度测试问题:在延迟渲染中,由于光照计算是在合成阶段进行的,因此无法直接利用深度缓冲区进行深度测试。这就意味着在渲染透明物体时,需要手动进行深度排序和深度测试,以确保正确的渲染顺序和透明效果。这增加了渲染透明物体的复杂性和计算开销。
-
透明物体的光照计算:透明物体的光照计算通常需要考虑到光线在物体内部的传播和反射等复杂情况。在延迟渲染中,这种复杂的光照计算需要额外的处理和优化,以确保正确的渲染效果和性能。
综上所述,延迟渲染在处理大规模透明物体时面临一些挑战,需要额外的处理和优化才能实现良好的渲染效果和性能。在一些情况下,采用其他渲染技术或混合渲染技术可能更适合处理大规模透明物体。
十二、EBO存储的数据是什么,有什么好处
EBO(Element Buffer Object)是OpenGL中用于存储索引数据的缓冲对象。它存储了顶点索引的信息,用于定义绘制三角形、四边形或其他形状的顶点连接关系。
EBO的好处包括:
节省内存:使用EBO可以避免存储重复的顶点数据。通过索引引用共享的顶点数据,可以大大减少存储相同顶点的内存占用。
提高效率:使用EBO可以减少传输和处理顶点数据的开销。由于只需要传输顶点索引,减少了数据传输量,从而提高了渲染效率。
简化绘制调用:使用EBO可以简化绘制调用。只需绑定EBO并调用绘制函数,而不需要重复指定顶点数据。这对于绘制复杂的几何体和模型非常有用。
支持复杂几何体:EBO允许描述复杂的几何体,例如多边形、扇形、环形和非连续的顶点连接。通过定义适当的索引顺序,可以构建各种复杂形状。
支持动态修改:EBO可以动态修改索引数据,而无需重新传输和处理整个顶点数据。这对于实现动态几何变化和动画效果非常有用。
总之,EBO的使用可以减少内存占用、提高渲染效率,并简化绘制调用。它是在OpenGL中进行高效渲染的重要工具之一,特别适用于复杂几何体的绘制和动态几何变化的场景。
十三、G-Buffer的底层结构
G-Buffer(几何缓冲区)是一种用于存储场景中几何信息的缓冲区,通常由多个纹理组成。其底层结构可以根据需要和实现方式而有所不同,但通常包括以下几个基本元素:
下图来自Unity官方文档Deferred Rendering Path in URP
十四、抗锯齿方法的种类,各自优缺点
抗锯齿(Anti-Aliasing)是用于减少图形渲染中锯齿(锯齿状边缘)的视觉效果的技术。以下是一些常见的抗锯齿方法及其各自的优缺点:
MSAA(多重采样抗锯齿):
优点:MSAA是一种硬件支持的抗锯齿技术,对性能影响相对较小。它可以提供较好的视觉质量,减少锯齿的出现。
缺点:MSAA只对几何边缘进行抗锯齿处理,对纹理边缘和细节部分效果较差。此外,MSAA在透明度和混合效果的处理上有一些限制。
FXAA(Fast Approximate Anti-Aliasing):
优点:FXAA是一种基于后处理的抗锯齿方法,适用于各种渲染场景。它可以实现较好的平滑效果,并且处理速度较快。
缺点:FXAA可能会导致图像细节的模糊和失真,尤其是在高对比度和细小纹理部分。
SMAA(Subpixel Morphological Anti-Aliasing):
优点:SMAA是一种后处理的抗锯齿技术,相对于FXAA提供了更好的视觉质量。它可以减少锯齿同时保持图像细节。
缺点:SMAA相对于FXAA来说计算量稍大,对性能可能有一定的影响。
TXAA(Temporal Anti-Aliasing):
优点:TXAA是一种结合多帧抗锯齿和时域抗锯齿的技术。它可以减少锯齿和马赛克效应,提供较高的图像质量。
缺点:TXAA的主要缺点是计算成本较高,对性能要求较高,可能会导致一些模糊和运动模糊的效果。
这些抗锯齿方法在实际应用中可以根据场景需求和性能要求进行选择。通常,MSAA适用于硬件支持的场景,FXAA和SMAA适用于后处理效果,而TXAA则适用于高质量图像的需求,但需要更高的计算性能。
十五、遮挡剔除算法种类
遮挡剔除(Occlusion Culling)是一种用于优化渲染性能的技术,旨在避免渲染那些在场景中被其他物体遮挡的不可见对象。以下是一些常见的遮挡剔除算法种类:
视锥剔除(View Frustum Culling):视锥剔除是最基本的遮挡剔除算法。它通过判断物体是否在相机的视锥体内来决定是否进行渲染。超出视锥体范围的物体会被剔除。
边界体剔除(Bounding Volume Culling):边界体剔除使用物体的边界体(如包围盒、包围球等)来判断物体是否与视锥体相交。如果边界体与视锥体不相交,则可以剔除该物体。
遮挡剔除缓冲(Occlusion Culling Buffer):遮挡剔除缓冲是一种基于像素的遮挡剔除算法。它使用附加的深度或遮挡缓冲来记录场景中每个像素的可见性,并根据这些信息决定是否渲染像素。
图块遮挡剔除(Portal Occlusion Culling):图块遮挡剔除是一种基于区域划分的遮挡剔除算法。它将场景划分为多个相互连接的区域,并使用门户(Portal)来判断区域之间的可见性。通过只渲染可见的区域,可以减少不必要的渲染开销。
GPU遮挡剔除(GPU Occlusion Culling):GPU遮挡剔除是一种使用GPU进行遮挡剔除的技术。它通过在GPU上执行遮挡剔除算法,使用硬件加速来进行渲染可见性的计算。
这些遮挡剔除算法可以根据场景的复杂性和性能需求来选择和组合使用。通常,结合多个遮挡剔除算法可以获得更好的性能优化效果。
更多推荐
所有评论(0)