解读Unity中的CG编写Shader系列二

转自 http://www.itnose.net/detail/6095974.html

上一篇文章的例子中我们可以看到顶点着色器的输出参数可以说是直接作为了片段着色器的形参传递过来,那么不由得一个问题浮现出来,顶点着色器的形参是从何处传递过来的?
顶点着色器的形参是gameObject 的meshRenderer组件将所有的mesh数据按每一帧一次传递给OpenGL。

这中间的过程常常被称作一次draw call,往往一次性传输大量mesh信息作为一次draw call 比多次传输少量mesh信息引起多次draw call更加效率。

而在上一个例子中我们只接受了MeshRenderer传递过来的 POSITION信息,实际上还有很多信息没有接受。想要在顶点着色器中接收Mesh信息的某些数据,只需要在形参后生命正确的语义即可获得对应的信息。

那么顶点着色器可以根据语义获取到的全部mesh信息有:

float4 vertex : POSITION; //顶点坐标

float4 tangent : TANGENT; // tangent,三角函数的一种,缩写为tan我们很熟悉了,他的值是mesh到表面法线的正切值

float3 normal : NORMAL; //表面法向量,以对象的坐标系标准化至单位长度

float4 texcoord : TEXCOORD0;//纹理坐标系的第0个集合

float4 texcoord1 : TEXCOORD1; //纹理坐标系的第1个集合

fixed4 color : COLOR;//颜色,通常为常数

同理我们可以声明一个顶点着色器的输入结构体,包含以上所有信息,然后将这个结构体作为形参传递给顶点着色器的入口函数。

Unity内建的预定义输入结构体:

只要引用UnityCg.cginc头文件(目录Unity > Editor > Data > CGIncludes下)就可以使用预先设定好的结构体直接使用,他们分别有appdata_base  appdata_tan和appdata_full:

struct appdata_base {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct appdata_tan {

float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct appdata_full {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 texcoord1 : TEXCOORD1;
fixed4 color : COLOR;

};

  

可以直接根据需要选择合适的输入结构体,我们可以书写这样形式的代码:

Pass{CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct vertexOutput {
float4 pos : SV_POSITION;
float4 col : TEXCOORD0;
};
vertexOutput vert(appdata_full input)
{
vertexOutput output;
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
output.col = input.texcoord;
return output;
}
float4 frag(vertexOutput input) : COLOR
{
return input.col;
}
ENDCG}

  

可以看到运行在球体上的效果:

例2:假彩色图像

我们了解假彩色图像之前更重要的是把颜色真正的当做一个向量,那么我们专注于这个向量的其中一个分量,令另外的分量固定不变。

例如当我们以球体(sphere)的输入信息中的texcoord参数以语义TEXCOORD0传给片段颜色时,颜色在红色方向的分量是最终texcoord的x坐标呈现的颜色。

意思就是说不管颜色是纯红,纯黄,或者纯洋红色,红色分量始终是1。(计算机的三原色是红绿蓝,黄色并不是三原色之一,黄色也由红色合成)

相对的不管颜色是纯蓝,纯绿,或者纯青色,红色分量始终是0。

那么我们看一下下面这行代码的作用:

output.col = float4(input.texcoord.x, 0.0, 0.0, 1.0);

显然我们取纹理坐标的x参数作为红色分量构造了一个颜色向量,绿色与蓝色分量恒定为0,不透明度恒定为1,那么在球体中出来的应该是一个以纹理坐标的x坐标为变元的一元线性颜色球体。(也可以理解为以地球的纬度为变元,构成了一个由黑色(0,0,0,1)至红色(1,0,0,1)构成的地球)

上图为水平反向旋转后看向这个假彩色球体的效果

当我们设想我们只关注颜色的红色分量的时候,我们可以看到转动球体从0°旋转至360°再回到0°的时候,红色分量由0变为1,周而复始。它非常类似于行星表面的纬度坐标。

同理下面一行代码的作用是以球面的经度为变元构成的黑色至绿色颜色渐变的线性颜色球体:

output.col = float4(0.0, input.texcoord.y, 0.0, 1.0);

可以想象这个球体的南极应该是黑色(0,0,0,1),北极应该是绿色(0,1,0,1)

上图为从球体的南极看向这个假彩色球体的效果

写到这里,其实假彩色(False Color)这个概念玩过PS的朋友应该可以马上联想到通道,其实就是红色通道 绿色通道和蓝色通道而已,只不过我们以向量的观点来看他们是颜色向量的分量而已。我们可以理解为颜色为3个通道的混合,也可以认为是3个分量的合向量

 

纹理坐标(texture coordinates)非常适合用来表现颜色,因为他们的值范围都是0~1,所以无需任何转换就能把一个纹理坐标与一个颜色一一映射。

坐标上的法向量同样可以用来表现颜色,但是需要一定的转换。因为法向量的值范围是-1至+1,假设一个坐标x,y,z的法向量是α,那么α的范围是(-1,-1,-1)至(1,1,1),非常轻松的数学转换就可以将他与一个颜色一一映射:

α与(1,1,1)进行矢量相加,然后与2相除,即可得到一个(0,0,0)至(1,1,1)的向量,再补充第四元不透明度即可称为一个RGBA颜色向量。所以代码为:

output.col = float4((input.normal + float3(1.0, 1.0, 1.0)) / 2.0, 1.0);

这里我不再把效果图给出来了,大家可以自己试试

由此可见,我们如果想让mesh信息中的数据以颜色体现出来,如果原信息的值域的每个分量不在[0,1] 只需要将原信息的值域变换至这个区间内,使其每个分量都不超过[0,1]这个区间,我们就能将mesh信息与颜色建立单向映射体现出来。

那么下面3行代码中返回的颜色是否是正确的颜色,只用判断col向量的分量是否超过了[0,1]区间,超出这个区间我们就只能看到黑色:

output.col = input.texcoord - float4(1.5, 2.3, 1.1, 0.0);  //rgba区间分别为[-1.5,0.5],[-2.3,-1.3],[-1.1,-0.1],[0,1],不能与颜色建立映射
output.col = float4(input.texcoord.z);//rgba区间分别为[0,1],[0,1],[0,1],[0,1],可以与颜色建立映射
output.col = input.texcoord / tan(0.0);//分母为0数学上无意义,因为值无穷大,所以区间是[0,∞]

再看复杂一点的,判断下面的输出颜色是否正确,需要一些点乘和叉乘的知识:

output.col = dot(input.normal, float3(input.tangent)) *input.texcoord;
output.col = dot(cross(input.normal, float3(input.tangent)),input.normal) * input.texcoord;
output.col = float4(cross(input.normal, input.normal), 1.0);
output.col = float4(cross(input.normal,float3(input.vertex)), 1.0);

不好意思我的点乘和叉乘已经全部还给老师了,看来是得把我送回学校去重新学学了!我解不出来~

弧度函数radians()与噪音函数noise()总是返回黑色:

output.col = radians(input.texcoord);
output.col = noise(input.texcoord);

通过这一部分的知识我们已经掌握了:

1.顶点着色器的输入参数有哪些

2.怎样将这些输入信息以颜色的形式表现出来

时间: 2024-08-18 09:56:32

解读Unity中的CG编写Shader系列二的相关文章

解读Unity中的CG编写Shader系列一

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作为向量类型做点乘.内积等处理更快速

解读Unity中的CG编写Shader系列四——unity中的圆角矩形shader

上篇文章中我们掌握了表面剔除和剪裁模式 这篇文章将利用这些知识实现一个简单的,但是又很常用的例子:把一张图片做成圆角矩形 例3:圆角矩形Shader 好吧我承认在做这个例子的时候走了不少弯路,由于本人对矩阵的知识掌握已经悉数还给老师,所以一开始用了一些笨办法计算圆角矩形区域. 我们知道TEXTCOORD0是一个以对象为坐标系的坐标,并且范围在该坐标的第一象限,取值为(0,0)到(1,1) 那么我们把每一张图片都看做一张1X1大小的矩形 我们要在1X1大小的矩形中擦除4个角,应该是这样: 以左上角

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

讨论完漫反射之后,接下来肯定就是镜面反射了 在开始镜面反射shader的coding之前,要扩充一下前面提到的知识,加深理解镜面反射与漫反射的区别. 引用一下一位前人博文中的一些基础概念,特别是关于冯氏反射模型的: 平行光(directional light) 一种是从特定方向射入并只会照亮面对入射方向的物体,我们称之为平行光(directional light). 环境光(ambient light) 另一种光是来自所有方向并且会照亮所有物体,不管这些物体的朝向如何,我们称之为环境光(ambi

解读Unity中的CG编写Shader系列6——漫反射

如果前面几个系列文章的内容过于冗长缺乏趣味着实见谅,由于时间原因前面的混合部分还没有写完,等以后再补充,现在开始关于反射的内容了. 折射与反射 在物理世界中,光的反射与折射往往是同时存在的,光源由真空或者空气中射入一种材料,光在进入这种材料的同时就发生了折射,折射的程度与各个介质的折射率有关,使光的传播路线偏离原来的路线: 继而如果光在通过不同传播介质的表面时,会像乒乓球一样弹回来,我们人眼能够看到东西,都是因为东西会反射光源,如果一种物质无法反射光,或者没有光源,我们就看不到东西.同样对于不同

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

1.不透明度 当我们要将两个半透的纹理贴图到一个材质球上的时候就遇到混合的问题,由于前面的知识我们已经知道了片段着色器以及后面的环节的主要工作是输出颜色与深度到帧缓存中,所以两个纹理在每个像素上的颜色到底以怎样的形式混合在一起最后输出到帧缓存中是我们现在首要讨论的内容. 1.混合(blending) 上一篇文章中的管道环节中的"逐帧操作"环节中的一项操作就是混合操作,可见混合操作是不可编程的固定功能环节. 在混合操作中,我们将片段着色器中计算出来的颜色称之为 "源颜色&quo

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

在上一个样例中,我们得到了由mesh组件传递的信息经过数学转换至合适的颜色区间以颜色的形式着色到物体上. 这篇文章将要在此基础上研究片段的擦除(discarding fragments)和前面剪裁.后面剪裁(front face culling and back face culling)来达到透明效果. 当一个mesh组件的信息被传递后,我们能够通过代码决定哪些部分渲染(render)出来.而哪些部分不要.这个过程就像把那些不要的部分剔除了,我们看不到他.尽管他的mesh信息还在.可是我们的G

解读Unity中的CG编写Shader系列三

转自http://www.itnose.net/detail/6096068.html 在上一个例子中,我们得到了由mesh组件传递的信息经过数学转换至合适的颜色区间以颜色的形式着色到物体上.这篇文章将要在此基础上研究片段的擦除(discarding fragments)和前面剪裁.后面剪裁(front face culling and back face culling)来达到透明效果. 当一个mesh组件的信息被传递后,我们可以通过代码决定哪些部分渲染(render)出来,而哪些部分不要,这

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

转自http://www.itnose.net/detail/6098539.html 1.不透明度 当我们要将两个半透的纹理贴图到一个材质球上的时候就遇到混合的问题,由于前面的知识我们已经知道了片段着色器以及后面的环节的主要工作是输出颜色与深度到帧缓存中,所以两个纹理在每个像素上的颜色到底以怎样的形式混合在一起最后输出到帧缓存中是我们现在首要讨论的内容. 1.混合(blending) 上一篇文章中的管道环节中的“逐帧操作”环节中的一项操作就是混合操作,可见混合操作是不可编程的固定功能环节. 在

解读Unity中的CG编写Shader系列十 (光滑的镜面反射(冯氏着色))

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