CG

UnityShader 提供的CG/HLSL语义

一、语义的解释

语义,其实就是一个赋给Shader输入和输出的字符串,这个字符串表达了这个参数的含义。通俗的讲这些语义可以让Shader知道从哪读取输送到哪里去,他们是在CG/HLSL的shader流水线中是不可避免。表示图元数据的含义(顶点的位置、法向量或者纹理信息),也表明这些图元数据存放的硬件资源。因为顶点着色器的输出即是片段着色器的输入,所以顶点着色器的输出必须和片段着色器的输入语义是一致的。

语义是顶点程序和片段程序之间输入\输出数据和寄存器之间的桥梁,因此语义只对这两个处理阶段有意义,并且只在入口函数才有效,在内部函数无效。语义概念的提出和图形流水线工作机制大有关系。从GPU 处理流程中可以看出,一个阶段处理数据,然后传输给下一个阶段,那么每个阶段之间的接口是如何确定的呢?

前文完成了最基本的镜面反射着色器,单平行光源下的逐顶点着色(per-vertex lighting),又称为古罗着色(Gouraud shading)。这篇文章作为后续讨论更光滑的镜面反射方式,逐像素着色(per-pixcel lighting),又称为冯氏着色(Phong shading)
逐像素着色Per-Pixel Lighting (冯氏着色Phong Shading)
别把冯氏着色与冯氏反射模型搞混淆了,前问提到了冯氏反射模型,冯氏反射模型是为使计算机模拟接近真实的物体表面光泽提出的模型,即环境光(虚拟的)+漫反射光+镜面反射光=表面色彩。

逐顶点着色,故名思意跟顶点有关,也就是在我们的顶点着色器中根据每个顶点上的入射向量L、法向量N、观察向量V等直接计算出每个顶点该有的颜色,然后传递给后续环节进行着色,可想而知由于顶点是离散的,片段是连续的,所以引起着色效果的不光滑很容易理解。

那么逐像素着色与之对应的即是在片段着色器中,对法向量与坐标进行插值(如果不能理解插值,请百度一下),从而使离散的顶点计算出来的离散的颜色变得连续而光滑。这就是冯氏着色的要义。

将前文的古罗着色改为冯氏着色

(原)解读Unity中的CG编写Shader系列9——镜面反射

讨论完漫反射之后,接下来肯定就是镜面反射了
在开始镜面反射shader的coding之前,要扩充一下前面提到的知识,加深理解镜面反射与漫反射的区别。
注:这篇文章实现的镜面反射是逐顶点着色(per-vertex lighting),最后效果图可以看到高亮区域并不光滑,更光滑的着色方式请看系列10逐像素着色(per-pixcel lighting),又称冯氏着色
引用一下一位前人博文中的一些基础概念,特别是关于冯氏反射模型的:
平行光(directional light)
一种是从特定方向射入并只会照亮面对入射方向的物体,我们称之为平行光(directional light)。

环境光(ambient light)
另一种光是来自所有方向并且会照亮所有物体,不管这些物体的朝向如何,我们称之为环境光(ambient light)。当然在真实世界里,这只是平行光照到其他物体上,比如空气、灰尘等等,然后反射出来的散射而已。但是在这里,我们需要把它单独作为一个光照模型列出来。
漫反射(Diffuse)
无论光的入射角度如何,都会向所有方向发生反射。反射光的亮度只和光线的入射角度有关,与观察角度无关。光线越平行于物体表面,则反射光越弱,表面越暗;光线越垂直于表面,反射光越强,表面越亮。漫反射是我们通常想到一个物体受到光照时需要首先想到的。

(原)解读Unity中的CG编写Shader系列8——多光源漫反射

前文中完成最简单的漫反射shader只是单个光源下的漫反射,而往往场景中不仅仅只有一个光源,那么多个光源的情况下我们的物体表面的漫反射强度如何叠加在一起呢?前文打的tag "LightMode"="ForwardBase"又是什么意思呢?
Unity内置的DiffuseShader,也就是我们创建一个Material出来时默认的Shader也是多光源的,所以这篇文章完成的shader与默认的diffuse shader基本效果一致。
首先引入几个概念
渲染路径 Rendering Path
Unity在处理多光源的情况时为我们提供了三种模式:修改的地方在 Edit--Project Settings--Player--Other Settings--Rendering Path
1.顶点光 Vertex Lit
2.方向性 Forward (默认)
3.延迟照明 Deferred Lighting
我们的shader也使用默认的Forward
像素光 Pixel Light
Unity中将平行光称作为像素光,第一个像素光是基础平行光,以LightMode=ForwardBase标签修饰,每多一个像素光都以LightMode=ForwardAdd标签修饰。

(原) 解读Unity中的CG编写Shader系列7——漫反射

如果前面几个系列文章的内容过于冗长缺乏趣味着实见谅,由于时间原因前面的混合部分还没有写完,等以后再补充,现在开始关于反射的内容了。
折射与反射
在物理世界中,光的反射与折射往往是同时存在的,光源由真空或者空气中射入一种材料,光在进入这种材料的同时就发生了折射,折射的程度与各个介质的折射率有关,使光的传播路线偏离原来的路线;
继而如果光在通过不同传播介质的表面时,会像乒乓球一样弹回来,我们人眼能够看到东西,都是因为东西会反射光源,如果一种物质无法反射光,或者没有光源,我们就看不到东西。同样对于不同的材料,光的通过性越高,反射越不明显,比如玻璃(但是玻璃同样会有一定的反射,不然我们是看不到玻璃的,实际上我们看得到);光的通过性越低,反射越明显,比如水面,我们可以看到水中的鱼(光从水射到鱼的皮肤,鱼的皮肤发生了漫反射),也可以看到自己的倒影(水面发生了镜面反射)。

镜面反射与漫反射
那么镜面反射与漫反射的区别就是光碰到的物体表面被弹回来的过程中,表面的平整程度,越平越接近镜面反射,越粗糙就是漫反射。我们现实生活中看到的绝大部分东西的表面都是粗糙的,所以都是漫反射。

(原)解读Unity中的CG编写Shader系列6——不透明度与混合

1.不透明度
当我们要将两个半透的纹理贴图到一个材质球上的时候就遇到混合的问题,由于前面的知识我们已经知道了片段着色器以及后面的环节的主要工作是输出颜色与深度到帧缓存中,所以两个纹理在每个像素上的颜色到底以怎样的形式混合在一起最后输出到帧缓存中是我们现在首要讨论的内容。

1.混合(blending)

上一篇文章中的管道环节中的“逐帧操作”环节中的一项操作就是混合操作,可见混合操作是不可编程的固定功能环节。

在混合操作中,我们将片段着色器中计算出来的颜色称之为 “源颜色”,帧缓存中对应的像素已经存在的颜色叫做“目标颜色”。混合操作就是将源颜色与目标颜色以一些选项进行结合。

既然是固定功能环节,那么这些选项我们只能选择,不能编写。

我们选择混合的选项的过程是通过以下面的等式来进行RGBA颜色的计算的:

float4 result = SrcFactor * fragment_output + DstFactor * pixel_color;

我还是用图形表达上面这个等式吧:

(原)解读Unity中的CG编写Shader系列5——理论知识

经过前面的系列文章中的三个例子,尽管代码简单,但是我想应该还有些地方没有100%弄明白,我们现在得回过头来补充一些必备的数学、图形学知识

1、图形管道
第一个例子中我有提到顶点着色和片段着色在整个图形绘制过程中属于一个环节,整个过程叫做管道,这个管道的所有环节包括:

在整个管道中,只有顶点着色与片段着色是可编程的,顶点数据和帧缓存是具体的数据,剩下的环节是固定功能的环节,即不能用cg去编程的环节。

2、数据流

3、语义、特殊参数(uniform)

(原)解读Unity中的CG编写Shader系列4——unity中的圆角矩形shader

上篇文章中我们掌握了表面剔除和剪裁模式
这篇文章将利用这些知识实现一个简单的,但是又很常用的例子:把一张图片做成圆角矩形

例3:圆角矩形Shader
好吧我承认在做这个例子的时候走了不少弯路,由于本人对矩阵的知识掌握已经悉数还给老师,所以一开始用了一些笨办法计算圆角矩形区域。
我们知道TEXTCOORD0是一个以对象为坐标系的坐标,并且范围在该坐标的第一象限,取值为(0,0)到(1,1)
那么我们把每一张图片都看做一张1X1大小的矩形
我们要在1X1大小的矩形中擦除4个角,应该是这样:

以左上角为例,我们做一个辅助圆内切于这个角,半径为0.1,那么我们将这个圆擦掉3/4,剩下的黄色弧线与这个角形成的区域就是我们要擦除的区域:

(原) 解读Unity中的CG编写Shader系列3——表面剔除与剪裁模式

在上一个例子中,我们得到了由mesh组件传递的信息经过数学转换至合适的颜色区间以颜色的形式着色到物体上。这篇文章将要在此基础上研究片段的擦除(discarding fragments)和前面剪裁、后面剪裁(front face culling and back face culling)来达到透明效果。
当一个mesh组件的信息被传递后,我们可以通过代码决定哪些部分渲染(render)出来,而哪些部分不要,这个过程就像把那些不要的部分剔除了,我们看不到他,虽然他的mesh信息还在,但是我们的GPU不会去处理它,肯定比剔除前GPU的性能消耗要低。
这个过程就好比我们的mesh组件是一个透明的膜,我们假设这个胶纸我们根本看不到,而片段着色器在着色的时候像毛笔选择性地上色,最后的效果是我们可能看到膜的一部分是可见的,但是不见的地方,膜还是存在的,只是我们没有给他上色,我们既看不看他们,也不需要再他们上面画宝贵的墨水(GPU并行处理能力)

所以我们可以来改造一下上一个例子中的经度绿色假彩色球体,将其经度>0.5的部分擦掉,那么代码应该相应修改为:

Pass{
Cull Off // 关掉裁剪模式,作用后面再说
CGPROGRAM
#pragma vertex vert

(原)解读Unity中的CG编写Shader系列1——初识CG

CG=C for Graphics 用于计算机图形编程的C语言超集
前提知识点:
1.CG代码必须用
CGPROGRAM
。。。
ENDCG括起来
2.顶点着色器与片段着色器的主函数名称可随意,但需要再#pragma vert 与#pragma fragment中声明并且与主函数名完全匹配,shader才会找到入口
3.float4是一种压缩数组,float4 vert与float vert[4]严格意义上讲不同,虽然都是存放4个float,但float4作为向量类型做点乘、内积等处理更快速
4.语义 :变量除了变量名与数据类型之外,还在:后声明其语义
例子一:RGB立方体
例题阐述:
给一个立方体(cube)创建一个shader,在立方体的直角坐标系中,三个坐标x,y,z的取值范围为{0,0,0}至{1,1,1},而RGBA颜色red,green,blue,alpha的取值范围正好是{0,0,0,0}至{1,1,1,1},将alpha写死为1,那么r,g,b分别与x,y,z建立一一映射,可得到一个6个面展示所有RGB颜色的立方体如图:

同步内容
--电子创新网--
粤ICP备12070055号