Unity3D Shader之路 写Shader前必须要知道的事情 渲染流水线的概括

版本:unity 5.4.1  语言:Unity Shader

总起:

最近花了一个月的时间把《Unity Shader 入门精要》看完了,没怎么写博文,因为写得太好了,看得有点废寝忘食了,再次强烈推荐。

今天把写Shader前必须要知道的渲染流水线给概括一下,然后简单结合顶点\片元着色器Shader,说说各个代码在流水线的位置,以及职责功能。

渲染流水线:

在写Unity脚本的时候,不管是C#也好还是js也好,都是在跟CPU打交道,做算术运算、调用类成员、控制程序流程。而写Shader不同,他是跟GPU打交道。

我们首先来看看脚本的一个调用流程:(百度上有中文翻译,我这边直接上官方网站了)https://docs.unity3d.com/Manual/ExecutionOrder.html

从这里看出游戏中的一帧首先是初始化,也就是Awake、OnEnable、Start这些函数的执行,接着物理、输入、游戏逻辑的执行,在Unity中写代码的一天到晚都是跟这些函数打交道吧?接下来我们看到了Scene Rendering,这里就是一帧最重要的渲染部分了:由CPU发出指令(也就是平常所说的drawcall),GPU响应指令进行渲染,执行的就是一个渲染流水线。

而Shader在其中对一个渲染流水线进行控制,告诉GPU,CPU传过来的这个物体该如何渲染。粗浅的来说Shader之于GPU,就如同C#之于CPU。

那么渲染流水线是怎样的一个流程呢?简单的来说就是:“顶曲几裁屏,三三片逐屏”。

这是一种简单的记忆方法,感觉要还蛮好用的。

第一行所说的是几何阶段:顶点着色器 -> 曲面细分着色器 -> 几何着色器 -> 裁剪 -> 屏幕映射。主要的工作就是将模型的各个顶点变换到屏幕坐标的一个空间中,为真正的渲染做准备。

第二行说的是光栅化阶段:三角形设置 -> 三角形遍历 -> 片元着色器 -> 逐片操作 -> 屏幕图像。这一个阶段就是真正的在屏幕显示像素。

一个简单的Shader:

这边就通过书上的例子简单的说说流水线各个部分的作用。

Shader "Unity Shaders Book/Chapter 5/Simple Shader"
{
	// 显示在Unity Inspector上的属性,给用户提供控制
	// 形式:属性名("Inspector显示的标签", 属性类型) = 初始化数据
	Properties
	{
		// 这是一个Color,初始化为(1,1,1,1),在Inspector上显示Color Tint,而在Shader中使用_Color进行调用
		_Color("Color Tint", Color) = (1, 1, 1, 1)
	}

	// Shader的控制代码都在Pass中,一个SubShader可能包含多个Pass
	// 根据控制代码的不同可能会调用所有的Pass,也可能只调用一个
	// 现在我们只有一个,进行渲染就会调用这个Pass,暂时不用多管其他的情况
	SubShader
	{
		// 标签,SubShader下的标签对所有Pass都有效,而Pass中的只对本身有效
		// Queue:说明渲染所在的队列,Geometry表明大多数不透明物体是在该队列中渲染的
		// RenderType Opaque:表明这个物体是不透明的,与队列的区别是,Queue只是渲染顺序,即使设置成其他的也没问题,
		//		              但你需要这个物体是不透明的RenderType就必须设置为Opaque
		Tags { "Queue" = "Geometry" "RenderType" = "Opaque" }
        Pass
        {
        	// LightMode:光照模式
        	Tags { "LightMode" = "ForwardBase" }

        	// 设置,控制Shader的一些默认处理,深度测试和深度写入总是开启
        	ZTest On ZWrite On

            // CGPROGRAM和ENDCG成对出现,两行内部的代码就是用cg语言(c语言的变种)写的控制代码
            CGPROGRAM

            // vertex:表明控制顶点着色器的函数是vert
            // fragment:表明控制片元着色器的函数是frag
            #pragma vertex vert
            #pragma fragment frag

            // 申明Properties中的属性,以便在vert和frag函数中调用
            uniform fixed4 _Color;

            // Unity给vert函数提供的输入
			struct a2v
			{
				// vertex必须,POSITION表明他是一个模型的顶点坐标
                float4 vertex : POSITION;
                // normal,NORMAL表明需要的是法线
				float3 normal : NORMAL;
				// texcoord,TEXCOORD0表明需要的是当前的模型的uv坐标
				float4 texcoord : TEXCOORD0;
            };

            // vert函数的输出,frag函数的输入
            // SV_开头的两个输出是必须的,而且必须这么写,这是流水线过程中必须获取的数据
            struct v2f
            {
            	// pos必须,SV_POSITION表明需要输出一个屏幕坐标
                float4 pos : SV_POSITION;
                // color,颜色,其他属性,在frag函数中使用,不过一般冒号后面的标识为TEXCOORD0、TEXCOORD1、TEXCOORD2...
                //        只要不重复就行了,其他属性标明什么无所谓
                fixed3 color : COLOR0;
            };

            // 顶点着色器控制的函数
            v2f vert(a2v v)
            {
            	// 先初始化输出结构
            	v2f o;

            	// UNITY_MATRIX_M:从模型空间到世界空间
            	// UNITY_MATRIX_V:从世界空间到相机的观察空间
            	// UNITY_MATRIX_P:从观察空间到屏幕空间
            	// o.pos:需要输出一个屏幕空间的坐标,所以很简单,UNITY_MATRIX_MVP是模型空间到屏幕空间的矩阵,然后乘以v.vertex就能得到
            	o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

            	// 根据法线计算颜色
            	o.color = v.normal * 0.5 + fixed3(0.5, 0.5, 0.5);

            	// 输出
                return o;
            }

            // 片元着色器使用的控制函数,必须写上SV_Target,表明该输出是一个屏幕上的颜色
            fixed4 frag(v2f i) : SV_Target
            {
            	// 获取vert处理后的颜色,再与_Color.rgb相乘
            	fixed3 c = i.color;
            	c *= _Color.rgb;

            	// 最后输出
                return fixed4(c, 1.0);
            }

            ENDCG
        }
    }

    // 申明备用的Shader,如果以上Pass无法运行的话
    Fallback "Diffuse"
}

最重要的是vertex和fragment所指定的两个函数vert和frag。它们所在的流水线位置分别是顶点着色器和片元着色器。

顶点着色器,对应vert函数,正如上文所说SV_POSITION变量是必须的,顶点着色器最重要的职责就是根据一个模型坐标输出屏幕坐标。

曲面细分着色器和几何着色器应该也是可以编写的,不过学这本书的时候没有用到,所以暂时忽略。

裁剪,vert把模型坐标变换到了屏幕坐标,这样就可以确定每个顶点是否在屏幕中,裁剪就是将不在屏幕中的顶点给裁剪掉。

屏幕映射,把每个图元的坐标转换到屏幕坐标中,经过MVP矩阵后,坐标是在(-1,-1)到(1,1)中的,而屏幕映射就是把它映射到屏幕分辨率中。比如分辨率为1920*1080,则需要把(-1,-1)至(1,1)映射到(0,0)至(1920,1080)。

三角形设置、三角形遍历。顶点组成三角形片,三角形片才组成网格,组成整个模型。这个两个阶段就是将顶点所组成三角形所覆盖的像素计算出来。

片元着色器,对应frag函数,对上两个阶段中传来像素进行处理,最终需要输出一个SV_Target所代表的最终颜色。

逐片操作,做深度测试、模板测试等,通过的像素才能显示在屏幕上,可以配置。对应上面的代码ZTest On开启深度测试。

屏幕图像,最终显示的结果。

总结:

顶曲几裁屏,三三片逐屏。记住这个的同时,需要知道vert函数做的是顶点变换对应顶点着色器这个阶段,而frag函数是对每个像素颜色进行处理,对应的是片元着色器这个阶段。

而裁剪和逐片操作这两个阶段虽然无法编写代码,但可以进行控制。

今天用自己的语言简单概括了一下渲染流水线,不知道大家能否通过这篇文章对渲染流水线有个简单的认识。写Shader,不知道流水线写的时候就根本不知道为什么要这样去写,所以掌握这个流水线是非常必要的。

以后会给大家带来更多的Shader理解和一些有趣的Shader使用。

时间: 2024-10-27 18:10:53

Unity3D Shader之路 写Shader前必须要知道的事情 渲染流水线的概括的相关文章

Unity3d 镜面反射 vertex and frag Shader源码

Unity3d 镜面反射 网上能找到的基本上是固定管道或表面渲染的shader, 特此翻译为顶点.片段渲染的Shader, 本源码只涉及shader与cs部分, Editor部分使用NGUI绘制的, 请自行下载NGUI unity3d 版本:v4.3.1 ReflectionEffect.cs using UnityEngine; using System.Collections; using System; /// <summary> /// 反射效果 /// </summary>

【浅墨Unity3D Shader编程】之十三 单色透明Shader &amp; 标准镜面高光Shader

本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://blog.csdn.net/poem_qianmo/article/details/50878538 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 本文工程使用的Unity3D版本: 5.2.1  本次更新放出的Shader为透明系列的3个Shader和标准的镜面高光Shader的两个Shader.由易到难,由入门级到应用级,难度梯度合理. 依然是先放出游戏场景的exe

Unity CG 写一个超酷的 ray-marching(shader纯代码写3D)

Unity CG 写一个超酷的 ray-marching(shader纯代码写3D) 1.其实自从看了http://www.shadertoy.com(inigo quilez为其主创始人)上的shader后,让我感到很高兴 2.更重要的是自从我接触了一个叫 inigo quilez 的shader技术后,让我觉得shader情感更深的浓厚了 3.http://www.iquilezles.org/ 哈哈,当然给大家一个崇拜的机会吧,你一定会学到你想学到的技术和秘密 哈哈,邪恶的专栏地址放送,一

Unity3d 镜面反射 vertex and frag Shader源代码

Unity3d 镜面反射 网上能找到的基本上是固定管道或表面渲染的shader. 特此翻译为顶点.片段渲染的Shader, 本源代码仅仅涉及shader与cs部分. Editor部分使用NGUI绘制的, 请自行下载NGUI unity3d 版本号:v4.3.1 ReflectionMirror.cs using UnityEngine; using System.Collections; using System; /// <summary> /// 反射效果 /// </summary

cocos2d-js Shader系列4:Shader、GLProgram在jsb(native、手机)和html5之间的兼容问题。cocos2d-js框架各种坑。

为了让jsb也能顺利跑起滤镜效果,在手机侧折腾了2天,因为每次在真机上运行总要耗那么半分钟,而且偶尔还遇到apk文件无法删除导致运行失败的情况. 这个调试起来,实在让人烦躁加沮丧. 还好,测试上百轮,翻jsb代码+各种猜测实验之后,终于顺利的把前3个系列的例子都通通在Android上跑起来了,也算是把兼容问题调通了. 如下图所示,右上角的小图是多纹理效果,通过扩展cc.Node自行控制绘制顶点实现的:下方的两个小图是普通的cc.Sprite,对其加入了shaderProgram 总结一下,这里有

UnityShader之Shader格式篇【Shader资料1】

关于Shader,在Unity里面我们一般叫做ShaderLab,只要你的职业是与渲染搭边,Unity就与ShaderLab有着直接的关联,你都应该试着去学会它,其实我们在新手未有入门的时候,我们总是认为这是个很遥远的东西,只要我们有一次理解了,我们就会发现ShaderLab其实并不难. 每一个Shader,我们从最简单的方式去着手理解,你只需要把它想象成一个无限循环执行的函数即可.可能这样也有部分同学还是很难理解.那我写个代码我们试着来理解它. public void ShaderLab(ob

Unity3d优化之路

Unity3d优化之路.U3D的架构部分已经讲了很多了,这里我想讲讲对于U3D优化的亲身体验. 优化之路分三块: 一.渲染级别. GUI部分:我使用的是NGUI,它对动态移动.旋转.缩放GUI支持的是比较差的,所以我尽量不要把过多的移动旋转缩放的部分写在GUI中,但很多情况下是避免不了的,比如:大量的伤害数字,物品掉落,图标的移动和旋转等,为了不让GUI去控制这些渲染物体,一小部分我使用3D面片代替,而大部分使用程序去生成面片渲染脱离了GUI的控制.另外在那些静态的GUI中,我使用了静态物体优化

流光shader 和 流光+扭曲shader

我认为这种shader能通过简单的方式呈现出不错的效果. 1.流光shader: Shader "Unlit/StreamShader" { //流光shader Properties { _MainTex ("Texture", 2D) = "white" {} _StreamTex("StreamTexture", 2D) = "white" {} _StreamColor("StreamCo

iOS网络开发(1)写代码前需要了解的

从本篇文章开始,介绍一些网络开发中用到的技术. iOS的网络开发中,特别是应用开发,使用Http协议作为主要的通信手段 写代码前,先了解一些与协议相关联的内容,包括: URL的概念 HTTP与HTTPS HTTP请求的内容及请求对象 HTTP应答的内容及请求对象 URL URL:统一资源标示符,可以表示一个资源的路径,这个路径可以是本地的.网络上的. URL是对可以从互联网上得到的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址. 互联网上每一个文件都有唯一一个URL,它包含的信息指出文