渲染流水线
一、渲染流水线
渲染流水线的工作任务在于由一个三维场景出发、生存(或者说渲染)一张二维图像。换句话说,计算机需要从一系列的顶点数据、纹理等信息出发,把这些信息最终转换成一张人眼可以看到的图像。而这个工作通常是由CPU和GPU共同完成。
一个渲染流程分三个阶段:应用阶段(Application Stage)、几何阶段(Geometry Stage)、光栅化阶段(Rasterizer Stage)。
1、应用阶段
从名字我们可以看出,这个阶段是由我们的应用主导的,因此通常由CPU负责实现。换句话说,我们这些开发者具有这个阶段的绝对控制权。这一阶段中,开发者有3个主要任务:
首先,我们需要准备好场景数据,例如摄像机的位置、视锥体、场景中包含了哪些模型、使用了那些光源等等;
其次,为了提高渲染性能,我们往往需要做一个粗粒度剔除工作,以把那些看不见的物剔除出去,这样就不需要再移交给几何阶段进行处理;
最后,我们需要设置好每个模型的渲染状态,这些渲染状态包括但不限于它使用的材质(漫反射颜色、高光反射颜色)、试用的纹理、使用的shader等。
这一阶段最重要的输出是渲染所需要的几何信息,即渲染图元(Rendering Primitives)。通俗来讲,渲染图元可以是点、线、三角面等。这些渲染图元将会被传递给下一个阶段——几何阶段。
2、几何阶段
几何阶段用于处理所有和我们要绘制的几何相关的事情。例如,决定需要绘制的图元是什么,怎么绘制它们,在哪里绘制它们。这一阶段通常在GPU上进行。
几何阶段负责和每个渲染图元打交道,进行逐顶点、逐多边形的操作。这个阶段可以进一步分成更小的流水线阶段。几何阶段的一个重要任务就是把顶点变换到屏幕空间中,再交给光栅器进行处理。通过对输入的渲染图元进行多步处理后,这一阶段将会输出屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等相关信息,并传递给下一阶段。
3、光栅化阶段
这一阶段将会使用上个阶段传递的数据来产生屏幕上的像素,并渲染出最终的图像。这一阶段也是在GPU上运行。光栅化的任务主要是决定每个渲染图元中的哪些像素应该被绘制在屏幕上。它需要对上一个阶段的逐顶点数据(例如纹理坐标、顶点颜色等)进行插值,然后再进行逐像素处理。
二、CPU和GPU之间的通信
渲染流水线的起点是CPU,即应用阶段。应用阶段大致可分为下面3个阶段:
(1)把数据加载到显存中
(2)设置渲染状态
(3)调用DrawCall
DrawCall是一个命令,它的发起方是CPU,就收方是GPU。这个命令仅仅会指向一个需要被渲染的图元列表,而不会再包含任何材质信息
三、GPU流水线
当给定一个DrawCall时,GPU就会根据渲染状态(例如材质、纹理、着色器等)和所有输入的顶点数据进行计算,最终输出成屏幕上显示的那些漂亮的像素。而这个计算过程,就是GPU流水线。
从图中可以看出,GPU的渲染流水线接收顶点数据作为输入。这些顶点数据是由应用阶段加载到显存中,再由DrawCall指定的。这些数据随后被传递给顶点着色器。
顶点着色器(Vertex Shader)是完全可编程的,它通常用于实现顶点的空间变换、顶点着色等功能。
曲面细分着色器(Tessellation Shader)是一个可选的着色器,它用于细分图元。
几何着色器(Geometry Shader)同样是一个可选的着色器,它可以被用于执行逐图元(Per-Primitive)的着色操作,或者被用于产生更多的图元。
裁剪(Clipping)这一阶段的目的是将那些不在摄像机视野内的顶点裁减掉,并剔除某些三角图元的面片。
屏幕映射(Screen Mapping)这一阶段是不可配置和编程的,它负责把每个图元的坐标变换到屏幕坐标系中。
三角形设置(Triangle Setup)和三角形遍历(Triangle Traversal)阶段也都是固定函数的阶段,片元着色器(Fragment Shader)则是完全可编程的,它用于实现逐片元的着色操作。最后,逐片元操作(Per-Fragment Operation)阶段负责执行很多重要的操作,例如修改颜色、深度缓冲、进行混合等。
四、什么是DrawCall
DrawCall本身的含义很简单,就是CPU调用图像编程接口,如OpenGL中的glDrawElements命令或者DirectX中的DrawIndexedPrimitive命令,以命令GPU进行渲染的操作。
一个常见的误区是,DrawCall中造成性能问题的元凶是GPU,认为GPU上的状态切换是耗时的,其实不是的,真正“拖后腿”其实是CPU。
问题一:CPU和GPU是如何实现并行工作的
想让CPU和GPU可以并行工作,解决方法是使用一个命令缓冲区(Command Buffer)。
命令缓冲区包含了一个命令队列,由CU向其中添加命令,而由GPU从中读取命令,添加和读取的过程是相互独立的。命令缓冲区使得CPU和GPU可以相互独立工作,当CPU需要渲染一些对象时,它可以向命令缓冲区添加命令,而当GPU完成了上一次的渲染任务后,它就可以从命令队列中再取出一个命令并执行它。
命令缓冲区的命令有很多种类,而DrawCall是其中一种,其他命令还有改变渲染状态等(例如改变使用的着色器,使用不同的纹理等)。
问题二:为什么DrawCall多了会影响帧率
在每次调用DrawCall之前,CPU需要向GPU发送很多内容,包括数据、状态和命令等。这一阶段,CPU需要完成很多工作,例如检查渲染状态等。而一旦CPU完成了这些准备工作,CPU就可以开始本次的渲染。GPU的渲染能力是很强的,渲染200个还是2000个三角网格通常没什么区别,因此渲染速度往往快于CPU提交命令的速度。如果DrawCall数量太多,CPU就会把大量时间花费在提交DrawCall上,造成CPU的过载。
问题三:如何减少DrawCall
减少DrawCall一个很显然的优化想法就是把很多小的DrawCall合并成一个大的DrawCall,这就是批处理的思想
在游戏开发过程中,为了减少DrawCall的开销,有两点需要注意:
(1)避免使用大量很小的网格。当不可避免地需要使用很小的网格结构时,考虑是否可以合并。
(2)避免使用过多的材质。尽量在不同的网格之间共用一个材质。
五、什么是shader
我们之所以要花很大篇幅来讲述GPU的渲染流水线,是因为shader所在的阶段就是渲染流水线的一部分,更具体来说,shader就是:
(1)GPU流水线上一些可以高度编程的阶段,而由着色器编译出来的最终代码是会在GPU上运行的(队于固定管线的渲染来说,着色器有时等同于一些特定的渲染设置);
(2)有一些特定类型的着色器,如顶点着色器、片元着色器等;
(3)依靠着色器我们可以控制流水线中的渲染细节,例如用顶点着色器来进行顶点变换以及传递数据,用片元着色器来进行逐像素的渲染。
- Unity Shader入门精要学习笔记 - 第7章 基础纹理
转自 冯乐乐的 <Unity Shader 入门精要> 纹理最初的目的就是使用一张图片来控制模型的外观.使用纹理映射技术,我们可以把一张图“黏”在模型表面,逐纹素地控制模型的颜色. 在美术 ...
- Unity Shader入门精要学习笔记 - 第6章 开始 Unity 中的基础光照
转自冯乐乐的<Unity Shader入门精要> 通常来讲,我们要模拟真实的光照环境来生成一张图像,需要考虑3种物理现象. 首先,光线从光源中被发射出来. 然后,光线和场景中的一些物体相交 ...
- Unity Shader入门精要学习笔记 - 第3章 Unity Shader 基础
来源作者:candycat http://blog.csdn.net/candycat1992/article/ 概述 总体来说,在Unity中我们需要配合使用材质和Unity Shader才能达 ...
- Unity Shader入门精要读书笔记(一)序章
本系列的博文是笔者读<Unity Shader入门精要>的读书笔记,这本书的章节框架是: 第一章:着手准备. 第二章:GPU流水线. 第三章:Shader基本语法. 第四章:Shader数 ...
- Unity Shader入门精要学习笔记 - 第11章 让画面动起来
转自 冯乐乐的 <Unity Shader入门精要> Unity Shader 中的内置变量 动画效果往往都是把时间添加到一些变量的计算中,以便在时间变化时画面也可以随之变化.Unity ...
- Unity Shader入门精要学习笔记 - 第9章 更复杂的光照
转载自 冯乐乐的<Unity Shader入门精要> Unity 的渲染路径 在Unity里,渲染路径决定了光照是如何应该到Unity Shader 中的.因此,如果要和光源打交道,我们需 ...
- Unity Shader入门精要学习笔记 - 第8章 透明效果
转载自 冯乐乐的 <Unity Shader入门精要> 透明是游戏中经常要使用的一种效果.在实时渲染中要实现透明效果,通常会在渲染模型时控制它的透明通道.当开启透明混合后,当一个物体被渲染 ...
- Unity Shader入门精要学习笔记 - 第4章 学习 Shader 所需的数学基础
摘录自 冯乐乐的<Unity Shader入门精要> 笛卡尔坐标系 1)二维笛卡尔坐标系 在游戏制作中,我们使用的数学绝大部分都是计算位置.距离.角度等变量.而这些计算大部分都是在笛卡尔坐 ...
- Unity Shader入门
Unity Shader入门 http://www.cnblogs.com/lixiang-share/p/5025662.html http://www.manew.com/blog-30559-1 ...
随机推荐
- VS2013 修改TFS的本地映射路径
在源代码管理器里面 找到你的本地工作区 然后点击编辑按钮 修改本地目录
- [CoreOS 转载] CoreOS实践指南(七):Docker容器管理服务
转载:http://www.csdn.net/article/2015-02-11/2823925 摘要:当Docker还名不见经传的时候,CoreOS创始人Alex就预见了这个项目的价值,并将其做为 ...
- windows7系统下如何安装windows xp系统(无法识别硬盘,删除隐藏分区)
一.硬盘模式的设置 要设置好硬盘模式,否则安装操作系统的时候,根本就不识别硬盘,自然无法安装操作系统了.此步骤主要是解决无法识别硬盘的问题. 首先,进入BIOS当中,一般在advanced当中,有一个 ...
- MySQL的表分区
什么是表分区通俗地讲表分区是将一大表,根据条件分割成若干个小表.mysql5.1开始支持数据表分区了.如:某用户表的记录超过了600万条,那么就可以根据入库日期将表分区,也可以根据所在地将表分区.当然 ...
- C#排序比较
与C#定义了相等性比较规范一样,C#也定义了排序比较规范,以确定一个对象与另一个对象的先后顺序.排序规范如下 IComparable接口(包括IComparable接口和IComparable< ...
- 自定义android RadioButton View,添加较为灵活的布局处理方式
android的RadioButton的使用历来都让人比较头疼,如在布局方面,图案.文字无法分别设置padding等,另外,低版本的android RadioGroup不支持换行排列的RadioBut ...
- [转]LINQ To SQL 语法及实例大全
转载自:http://blog.csdn.net/pan_junbiao/article/details/7015633 LINQ to SQL语句(1)之Where Where操作 适用场景:实现过 ...
- struts2:非表单标签
非表单标签主要用于输出在Action中封装的信息,这在实际运用中是很常见的. 1. actionerror标签 <s:actionerror>标签主要用于输出错误信息到客户端,该标签将Ac ...
- Swift 3 新特性和迁移详解
写在前面 Swift 3.0 正式版发布了差不多快一个月了,断断续续的把手上和 Swift 相关的迁移到了Swift 3.0.所以写点小总结. 背景 代码量(4万行) 首先,我是今年年初才开始入手 S ...
- FFrpc python客户端lib
摘要: Ffrpc可以很方便的构建c++ server, 在网游服务器程序开发中,进程间通讯非常的重要,比如gateserver和gameserver或dbserver之间的通信.而ffrpc可以使得 ...