从0开始的技术美术之路(一)

渲染流水线

本篇参考《Shader入门精要》·冯乐乐女神著;

B站视频 “技术美术百人计划”·霜狼_may ;

本人数媒专业,TA小白,该文章用于自我参考复习,如有问题,请多多指教

一.大致介绍

1.整体流程(渲染管线可分为四个阶段)

  • 应用阶段: 粗粒度剔除,进行渲染设置,准备基本数据,输出到几何阶段
  • 几何阶段: 顶点着色器,曲面细分,几何着色器,顶点裁剪,屏幕映射
  • 光栅化阶段:三角形(点/线)设置,三角形(点/线)遍历,片段着色器
  • 逐片元操作:裁剪测试,透明度测试,深度测试,模板测试,混合(一般分法都参考于《Render-Time Rendering》将该操作归入光栅化中)
  • (后处理)

    在这里插入图片描述
    2.渲染流水线最终目的:输入虚拟摄像机(计算机动画课程里也可讲“视点”),光源,shader以及纹理,生成或渲染一张二维纹理;

二.分阶段介绍

1.应用阶段

在这里插入图片描述
在渲染管线中,应用阶段是在CPU中进行处理的

  • 首先是准备场景,从磁盘或是内存上读取模型或者贴图数据,将其加载进应用程序中,对象为基本数据:包括不限于场景中的物体的位置,朝向,大小,物体网格数据,顶点位置,UV贴图,法线,切线等;场景的光源位置朝向,类型,参数属性等;摄像机位置朝向,模式,视口长宽比等等…
补充:光源和阴影
- 设置光源:
1.方向光,颜色,方向等
2.点光源:颜色,位置,范围等
3.聚光源:颜色,位置,方向,内外圆锥角等
- 设置阴影:
 1.是否需要阴影:判断该光源可见范围内是否有可投阴影的物体
 2.阴影参数:对应光源序号,阴影强度,级联参数,深度偏移,近平面偏移等
 - 逐光源绘制阴影范围:
 1.近平面偏移
 2.逐级联
     - 计算当前光源+级联对应的观察矩阵,投影矩阵,以及对应到阴影贴图里的视口区域
     - 绘制到阴影贴图
  • 加速算法,粗粒度剔除:处理遮挡的问题,不会看到的,就不渲染,降低渲染成本,提高渲染性能
- 可见光裁剪:点光和聚光有衰减,聚光锥体有区域;若离相机距离较远,或者光锥与相机的视锥体不相交,则剔除渲染;
- 可见场景物体裁剪:遮挡啥的,相关算法:八叉树,BSP树,k-D树,PVH包围盒等

 说起裁剪就不由想起图形学里的经典裁剪算法:Liang-Barsky算法,Cohen-Sutherland算法(编码裁剪算法)这两个算法是用于将图像裁剪到可见范围内
 (遮挡剔除应该不涉及吧,遮挡应该是采用Zbuffer深度缓冲区进行检测 )
  • 设置渲染状态,准备渲染参数:渲染UI和场景,其参数和模式可能不一样,通俗解释:场景中的网格如何被渲染,渲染顺序是啥,使用哪个顶点着色器/片元着色器,光源属性,材质,最后会渲染到哪里,渲染模式等等…
-绘制设置:使用着色器,合批方式
- 绘制物体顺序:相对相机距离,材质RenderQueue,UICanvas等
- 渲染目标:FrameBuffer,RenderTexture...
- 渲染模式:前向渲染(ForwardBase ForwardAdd),延迟渲染

在这里插入图片描述

  • 调用DrawCall,输出渲染图元到显存:图元从CPU迈向GPU
- 顶点数据:位置,颜色,法线,纹理uv坐标,其他顶点数据
- 其他数据:MVP变换矩阵,纹理贴图,其他数据

在这里插入图片描述

2.几何阶段

在这里插入图片描述几何阶段,光栅化,逐片元这些操作都是在GPU中进行处理的,那么在讲几何阶段前,我们先聊聊为什么要用GPU渲染?

答:GPU的特点是并行性较好。当我们在对顶点数据进行处理时,他们虽然数据不同,但光照,几何运算方式啥都一样的时候,那么我们将他们放在GPU的不同工作单元上进行同时执行,速度会更快。

在这里插入图片描述

  • 顶点着色器:必须完成的一个工作:将顶点坐标从模型空间转换到齐次裁剪空间(投影坐标系),同时它还有计算顶点光照的功能,这需要获取应用阶段中光源位置朝向,摄像机位置朝向,当前顶点的世界位置(获取该位置需知道顶点在模型空间的位置,模型本身的位置旋转缩放…)
    在这里插入图片描述
顶点着色器-视图变换:模型坐标系--(模型变换) --> 世界坐标系
--(视图变换)-->视图坐标系 --(投影变换)-->投影坐标系--(视口变换)-->视口坐标系

上图中的前三个变换对应MVP(model view projection)矩阵,在顶点着色器中,顶点从模型坐标系转换到投影坐标系,最后一步由Unity帮忙完成

(碎碎念:看到这一系列变换,第一时间我就想起计算机动画技术课本上那串空间变换,一样的流程,那里面还要多计算坐标系的建立,先旋转还是先平移等问题,左右手坐标系不同,很容易绕晕)
  • 曲面细分着色器:通过现有顶点生成更多的顶点,需获取顶点在模型中的位置信息
它是一个可选着色器,使用顶点着色器输出的顶点,按照一定规则算法生成更多顶点,将现有的网格和图元细分(其效果类似于MAYA中的“平滑”效果,模型圆滑了但面数增大了)
  • 几何着色器(基于图元):通过现有的图元做些几何方面操作,生成更多顶点和图元,比如对现有图元所在平面生成法线,需获取现有图元顶点位置
-图元是啥?
答:可以为顶点,线段,俩顶点,三角形...基础的几何图形
  • 投影:将3D空间投到2D空间
    在这里插入图片描述
- 对于顶点在裁剪空间里的位置,xyzw进行透视除法:xyz除以w完成投影。使其从投影坐标系转换到标准设备坐标系(NDC);
- 由于正交和透视视角下w值不同,故而呈现效果不同,正交显得像截图,透视则近大远小
  • 顶点裁剪:消去屏幕外的顶点;
- 投影里又说要进行除法操作,当xyz超过-1~1范围,则判断其不在范围内,
舍弃进行经典的图形学裁剪算法;
- 投影和顶点裁剪在《Shader入门精要》中都是归属于裁剪一节
中,这个操作过程无法由代码控制,是硬件上固定操作,但可以自定裁剪操作进行配置
(套娃,硬件裁下,Unity里自己再写个二次裁剪,~不是~)
- 设备坐标系在opengl和DirectX中不一样,opengl xyz三维度取值范围都是-1~1,DirectX只有xy是-1~1,z为0~1
  • 屏幕映射:将顶点位置从3D坐标空间转换到2D坐标空间;
    opengl和DirectX原点不同,opengl左下方,DirectX左上方
    在这里插入图片描述

按照《Shader入门精要》所言:屏幕映射任务是把每个图元的x和y坐标转换到屏幕坐标系。

如应用阶段为几何阶段准备数据一样,几何阶段同样会为光栅化阶段准备数据;

3.光栅化阶段:

  • 三角形设置:拿到映射于2D空间里的顶点位置,组装成三角形;
  • 三角形遍历:寻找被三角形覆盖的所有像素的过程,知晓包含哪些2D空间像素点;

《Shader入门精要》:三角形遍历阶段将检查每个像素是否被一个三角网格所覆盖,若覆盖,则生成一个片元。

片元非像素,它是包含很多状态的集合,这些状态用于计算每个像素的最终颜色,这些状态包含不限于,屏幕坐标,深度信息,从几何阶段输出的顶点信息,法线,纹理坐标等。屏幕同一个像素位置可能有对应多个三角形的不同片元(如两片重叠的交集)

(遍历这一阶段的举例图像,很难不让人想到那些扫描算法)

  • 抗锯齿(MSAA):
    1.SSAA:

    • 渲染到一个分辨率放大n倍的buffer:屏幕分辨率1024x1024,渲染得到buffer可为2048x2048,放大四倍,对其采样后再输出屏幕
    • 对方打n倍的buffer下采样

    2.MSAA

    • 只有它发生在光栅化阶段
    • 计算多个覆盖样本:覆盖测试看子采样点是否在三角形以内,遮挡测试看这个子采样度的深度和,即与深度缓存中数值进行比较,看能否通过,若能通过两测试,则说明采样点属于三角形,得到覆盖信息并保存,用于之后的着色混合

    3.FXAA/TXAA
    后处理技术

  • 片段着色器:使用这些点的数据进行着色,并为后面片元着色器准备;

4.逐片元操作

主要任务:
1.决定每个片元可见性
2.若通过所有测试,则将其颜色值和已存储在颜色缓冲区中的颜色进行合并混合

  • 在逐片元操作中,我们通过裁剪,透明度,深度,模板各种测试来筛选我们所需要的的片元;
  • 由于在2D屏幕坐标系中,同一位置的像素点可能存在多个不同的片元(一对多),我们会将筛选过后的片元进行混合以得到一个类似贴图的数据,并将其保存在内存中;

5.后处理

Bloom,HDR,FXAA,景深,边缘检测,径向模糊

<think>嗯,用户想学习Unity中Compute Shader的基础知识,并实现个简单的效果。作为初学者教程,我需要从基础开始,逐步引导他们完成步骤。 首先,我应该解释Compute Shader是什么,以及它在Unity中的作用。根据引用[3],计算着色器可以将工作从CPU卸载到GPU,这对于图形和其他计算任务非常有用。用户可能需要了解为什么使用Compute Shader,比如性能优势和应用场景。 接下来,要介绍Compute Shader的基本结构。引用[1]和[2]提到Unity Shader的结构,比如Properties和SubShader,但Compute Shader可能有所不同。我需要说明如何创建个Compute Shader文件,并解释其中的关键部分,比如kernel函数、线程组和缓冲区。 然后,实现简单效果的步骤应该分点列出。比如,创建Compute Shader、定义变量、编写核函数、分配内存、创建脚本调用等。用户可能需要具体的代码示例,所以需要提供C#脚本和Compute Shader代码,并解释每部分的作用。 还要注意线程组的调度,比如numthreads和Dispatch的用法,这部分对初学者可能不太容易理解,需要用简单的方式说明。例如,线程组的维度如何影响计算,以及如何确定分发的组数。 另外,可能涉及的数据传输,比如使用ComputeBuffer,需要强调如何将数据从CPU传递到GPU,并在计算后读取回来。同时,要提醒释放缓冲区的必要性,避免内存泄漏。 最后,常见问题和优化建议可以帮助用户避免初学者的错误,比如线程组大小不合适、数据未初始化等。引用[3]提到的高级主题可以作为进步学习的建议,但当前应聚焦基础。 整个过程中,要确保使用正确的LaTeX格式,行内公式用$,独立公式用$$,并检查语法正确。保持回答结构清晰,分步骤说明,让用户能逐步跟随操作。同时,生成的相关问题要围绕Compute Shader的基础、优化和应用,帮助用户深入思考。</think># Unity Compute Shader 基础教程(简单效果实现) ## 、Compute Shader 基础概念 Compute Shader 是运行在 GPU 上的通用计算程序,可并行处理大量数据。相比传统渲染管线,它能实现粒子系统、物理模拟等复杂计算[^3]。 主要特点: - 独立于渲染管线 - 支持 $numthreads(x,y,z)$ 线程组定义 - 通过 $RWStructuredBuffer$ 与 CPU 交互 ## 二、简单效果实现步骤(以颜色渐变为例) ### 1. 创建 Compute Shader ```hlsl // SimpleCompute.compute #pragma kernel CSMain RWTexture2D<float4> Result; float Speed; [numthreads(8,8,1)] void CSMain (uint3 id : SV_DispatchThreadID) { float time = _Time.y * Speed; float4 color = float4( sin(time + id.x/64.0), cos(time + id.y/64.0), sin(time * 0.5), 1); Result[id.xy] = color; } ``` ### 2. 创建调用脚本 ```csharp // ComputeController.cs public class ComputeController : MonoBehaviour { public ComputeShader computeShader; public RenderTexture renderTexture; public float speed = 1.0f; void Start() { renderTexture = new RenderTexture(256, 256, 0); renderTexture.enableRandomWrite = true; renderTexture.Create(); } void Update() { int kernel = computeShader.FindKernel("CSMain"); computeShader.SetFloat("Speed", speed); computeShader.SetTexture(kernel, "Result", renderTexture); computeShader.Dispatch(kernel, 32, 32, 1); // 256/8=32 } void OnDestroy() { renderTexture.Release(); } } ``` ### 3. 场景配置步骤 1. 创建新材质 2. 将生成的 RenderTexture 拖到材质主贴图 3. 将材质赋予 Plane 等测试物体 ## 三、核心参数说明 | 参数 | 说明 | 公式示例 | |------|------|----------| | numthreads | 单个线程组维度 | $8 \times 8 \times 1$ | | Dispatch | 线程组分发数量 | $\frac{纹理尺寸}{线程组尺寸}$ | | _Time | 内置时间变量 | $\text{_Time.y} = \text{累计秒数}$ | ## 四、常见问题 1. **线程组配置错误**:确保 $Dispatch(x,y,z) \times numthreads(x,y,z) \geq$ 数据总量 2. **内存未初始化**:使用前必须调用 `RenderTexture.Create()` 3. **缓冲区未释放**:必须实现 `OnDestroy()` 释放资源 ## 五、优化建议 - 使用 $StructuredBuffer$ 代替 Texture 处理纯数据 - 合并内存访问模式(提升数据局部性) - 合理选择线程组大小(通常 8x8 或 16x16)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值