1. 概述

在上一篇文章《Unity3D学习笔记2——绘制一个带纹理的面》中介绍了如何绘制一个带纹理材质的面,并且通过调整光照,使得材质生效(变亮)。不过,上篇文章隐藏了一个很重要的细节——Unity Shader。Shader(着色器)是渲染管线中可被用户编程的阶段,依靠着色器可以控制渲染管线的细节。现代图像渲染技术,都把Shader封装成与Material(材质)相关的组件。所以这篇文章,我们就初步学习下在Unity中使用Shader。

2. 详论

2.1. 创建材质

在上一章中,材质、以及材质相关的资源是在Unity3D编辑器中创建,在C#脚本中直接引用的。这里为了学习使用Shader,我们使用自定义的Shader,可以在C#脚本中创建材质。修改上一章代码的材质部分:

Shader shader = Shader.Find("Custom/MainShader");
Material material = new Material(shader); Texture2D texture = Resources.Load<Texture2D>("ImageDemo");
material.mainTexture = texture; MeshRenderer meshRenderer = newGameObject.AddComponent<MeshRenderer>();
meshRenderer.material = material;

可以看到,要创建一个Material,首先得创建一个Shader。我们在Project视图中右键菜单->Create->Standard Surface Shader,创建一个标准表面着色器MainShader:

双击打开这个Shader,可以看到这个Shader的具体内容。标准着色器很复杂,我们清空里面的内容,填入我们这个更简单的着色器示例:

Shader "Custom/MainShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags{"Queue" = "Geometry"} Cull Back Pass
{
CGPROGRAM #pragma vertex vert
#pragma fragment frag sampler2D _MainTex; //顶点着色器输入
struct a2v
{
float4 position : POSITION;
float3 normal: NORMAL;
float2 texcoord : TEXCOORD0;
}; //顶点着色器输出
struct v2f
{
float4 position: SV_POSITION;
float2 texcoord: TEXCOORD0;
}; v2f vert(a2v v)
{
v2f o;
o.position = mul(UNITY_MATRIX_MVP, v.position);
o.texcoord = v.texcoord; return o;
} fixed4 frag(v2f i) : SV_Target
{
return tex2D(_MainTex, i.texcoord);
} ENDCG
}
}
FallBack "Diffuse"
}

2.2. 着色器

Unity使用的着色器语言叫做ShaderLab,它是图形渲染中Shader(例如GLSL,HLSL以及CG)的更高级更抽象一级的封装。ShaderLab是个非常简单的说明性描述语言,通过嵌套在花括号中的语义来描述Unity Shader文件。

2.2.1. 名称

通过Shader语义指定Unity Shader的名称:

Shader "Custom/MainShader"
{ }

这个名称非常重要,在Unity编辑器中需要通过这个名字来引用Shader。

2.2.2. 属性

Shader语义块的第一个语义块是Properties语义块,它连接着材质和Unity3d编辑器,设置了这个属性就能够通过材质面板调整材质,调整材质的本质就是调整Shader。Properties的定义通常描述如下:

Properties {
Name ("display name",PropertyType) = DefaultValue
}

Name指的是在Shader中使用的名称,display name指的是显示在材质面板的名称。PropertyType则有点容易混淆,它指的是显示在材质面板中的属性类型,借用一下《Unity Shader入门精要》的图表:

2.2.3. SubShader

每个Unity Shader都至少包含一个SubShader语义块,Unity会优先选择第一个能够在当前平台下运行的SubShader作为最终渲染效果的Shader。

这个语义块下面又会包含三个语义块:

2.2.3.1. 标签(Tags)

SubShader的标签用于用于标识何时以何种方式被渲染到渲染引擎,它由一系列键值对组成。Queue是最常用的标签,用于标识渲染物体在渲染队列中的位置:

我们这里,把这个渲染物体放到Geometry队列中,这个位置通常放置不透明物体的渲染:

Tags{"Queue" = "Geometry"}

2.2.3.2. 渲染状态(RenderSetup)

渲染状态用于设置图形硬件的各种状态,例如是否应开启 Alpha 混合或是否应使用深度测试等。在像OpenGL这样的图形接口中,通常是以函数的形式进行调用的,Unity3d将其放在Shader里面,也有一定的道理。

这里的渲染状态设置成将背面裁剪掉:

Cull Back

2.2.3.3. 通道(Pass)

在Pass语义块中,才是像OpenGL/DirectX中使用的Shader。OpenGL使用的着色器语言叫做GLSL,DirectX使用的着色器语言叫做HLSL,Unity3D则推荐使用Cg语言,这是一种类C语言,与HLSL非常相似。Cg语言代码段在Pass语义块中被包裹在CGPROGRAM和ENDCG之间:

CGPROGRAM
//...
ENDCG

2.2.4. 回退(FallBack)

FallBack定义了一种退化策略,由于不同机器支持的性能特性不同,如果之前的子着色器都不生效,那么就使用这个着色器,通常这个着色器是内置的:

FallBack "Diffuse"

2.3. 渲染管线

图形渲染引擎的渲染管线其实是个内涵非常丰富的概念,再次借用《Unity Shader入门精要》的插图,渲染管线的描述大致如下:

当然只看这个图是不够的,但是我们可以直接从代码层面去了解它。镶嵌在CGPROGRAM和ENDCG之间的CG代码,体现的正是渲染管线的思维。

首先,通过编译指令,分别指定顶点着色器程序和片元着色器程序:

#pragma vertex vert
#pragma fragment frag

vert就是顶点着色器的函数,在这个着色器程序中指定了计算了顶点坐标和纹理坐标:

v2f vert(a2v v)
{
v2f o;
o.position = mul(UNITY_MATRIX_MVP, v.position);
o.texcoord = v.texcoord; return o;
}

传入参数是一个结构体,POSITION,NORMAL,TEXCOORD0是Unity Shader中固定的语义,分别代表这位置、法向量以及纹理坐标,他们也被称为顶点属性。还记得在上一篇文章《Unity3D学习笔记2——绘制一个带纹理的面》中创建Mesh时给Mesh创建的成员变量vertices、uv和normals吧?给他们传入的数据正是在这里用到了。

//顶点着色器输入
struct a2v
{
float4 position : POSITION;
float3 normal: NORMAL;
float2 texcoord : TEXCOORD0;
};

传出参数则是另外一个结构体:

//顶点着色器输出
struct v2f
{
float4 position: SV_POSITION;
float2 texcoord: TEXCOORD0;
};

SV_POSITION表示的是裁剪空间坐标,也就是在顶点着色器中计算的顶点值。这个计算内容的内涵也挺丰富的,简单来说,创建Mesh时的顶点坐标,经过一个模型变换(Model)、视图变换(View)、投影变换(Projection),最终变成了裁剪空间坐标系中的坐标,体现在着色器中,就是内置的MVP矩阵UNITY_MATRIX_MVP。

剩下的就是片元着色器函数的部分了。在这个着色器中,_MainTex也就是我们先前创建的,并且传递到材质中的纹理,通过将顶点着色器中传递过来的纹理坐标进行采样,得到具体的片元颜色:

sampler2D _MainTex;

fixed4 frag(v2f i) : SV_Target
{
return tex2D(_MainTex, i.texcoord);
}

最终显示的效果如下:

可以看到这里显示的就是图片本身的颜色,这是因为在着色器中只是采样了图片的颜色,并没有光照计算的参与。也就是在图形引擎中,任何效果的设置只是表象,任何效果的实现都会归结到着色器中。

Unity3D学习笔记3——Unity Shader的初步使用的更多相关文章

  1. Unity3D学习笔记6——GPU实例化(1)

    目录 1. 概述 2. 详论 3. 参考 1. 概述 在之前的文章中说到,一种材质对应一次绘制调用的指令.即使是这种情况,两个三维物体使用同一种材质,但它们使用的材质参数不一样,那么最终仍然会造成两次 ...

  2. Unity3D学习笔记7——GPU实例化(2)

    目录 1. 概述 2. 详论 2.1. 实现 2.2. 解析 3. 参考 1. 概述 在上一篇文章<Unity3D学习笔记6--GPU实例化(1)>详细介绍了Unity3d中GPU实例化的 ...

  3. Unity3D学习笔记8——GPU实例化(3)

    目录 1. 概述 2. 详论 2.1. 自动实例化 2.2. MaterialPropertyBlock 3. 参考 1. 概述 在前两篇文章<Unity3D学习笔记6--GPU实例化(1)&g ...

  4. Unity3D学习笔记12——渲染纹理

    目录 1. 概述 2. 详论 3. 问题 1. 概述 在文章<Unity3D学习笔记11--后处理>中论述了后处理是帧缓存(Framebuffer)技术实现之一:而另外一个帧缓存技术实现就 ...

  5. Unity3D学习笔记4——创建Mesh高级接口

    目录 1. 概述 2. 详论 3. 其他 4. 参考 1. 概述 在文章Unity3D学习笔记2--绘制一个带纹理的面中使用代码的方式创建了一个Mesh,不过这套接口在Unity中被称为简单接口.与其 ...

  6. 【HLSL学习笔记】WPF Shader Effect Library算法解读之[DirectionalBlur]

    原文:[HLSL学习笔记]WPF Shader Effect Library算法解读之[DirectionalBlur] 方位模糊是一个按照指定角度循环位移并叠加纹理,最后平均颜色值并输出的一种特效. ...

  7. 【HLSL学习笔记】WPF Shader Effect Library算法解读之[Embossed]

    原文:[HLSL学习笔记]WPF Shader Effect Library算法解读之[Embossed] Embossed(浮雕效果)          浮雕效果主要有两个参数:Amount和Wid ...

  8. 【HLSL学习笔记】WPF Shader Effect Library算法解读之[BandedSwirl]

    原文:[HLSL学习笔记]WPF Shader Effect Library算法解读之[BandedSwirl] 因工作原因,需要在Silverlight中使用Pixel Shader技术,这对于我来 ...

  9. unity3d学习笔记(一) 第一人称视角实现和倒计时实现

    unity3d学习笔记(一) 第一人称视角实现和倒计时实现 1. 第一人称视角 (1)让mainCamera和player(视角对象)同步在一起 因为我们的player是生成的,所以不能把mainCa ...

随机推荐

  1. .Net RabbitMQ实战指南——HTTP API接口调用

    RabbitMQ Management插件还提供了基于RESTful风格的HTTP API接口来方便调用.一共涉及4种HTTP方法:GET.PUT.DELETE和POST.GET方法一般用来获取如集群 ...

  2. MySQL 全文索引实现一个简单版搜索引擎

    前言 只有Innodb和myisam存储引擎能用全文索引(innodb支持全文索引是从mysql5.6开始的) char.varchar.text类型字段能创建全文索引(fulltext index ...

  3. 6.10考试总结(NOIP模拟6)

    前言 就这题考的不咋样果然还挺难改的.. T1 辣鸡 前言 我做梦都没想到这题正解是模拟,打模拟赛的时候看错题面以为是\(n\times n\)的矩阵,喜提0pts. 解题思路 氢键的数量计算起来无非 ...

  4. 关于win10 samba访问提示用户名和密码错误的原因

    排除掉linux上的配置错误,只需要做到以下两步就可以了: 1.win10系统运行secpol.msc 打开本地安全策略 2.安全策略->本地策略->安全选项 3.右侧找到 网络安全:LA ...

  5. 什么是 Shell 脚本?

    Shell 既是一种命令语言,又是一种程序设计语言.Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务.Windows Explorer 是一个典型的图形 ...

  6. 查看JVM中的线程名

    实例说明 在Java虚拟机中(JVM):除了用户创建的线程,还有服务于用户线程的其他线程.它们根据不同的用途被分到不同的组中进行管理.本实例将演示JVM中线程的名字及其所在组的名称. 关键技术 线程组 ...

  7. AcWing 237. 程序自动分析

    #include<bits/stdc++.h> using namespace std; const int N=1e6+5; int f[N*2],a[N],b[N],c[N],n,t, ...

  8. CentOS-yum安装chrome+chromeDriver+xvfb

    安装chrome 创建yum源文件 $ vim /etc/yum.repos.d/google-chrome.repo [google-chrome] name=google-chrome baseu ...

  9. Docker:Linux离线安装docker-17.03.2-ce,配置开机自启

    1. 准备docker离线包 docker官方离线包下载地址 下载需要安装的docker版本,我此次下载的是:docker-17.03.2-ce.tgz 2. 准备docker.service 系统配 ...

  10. 官宣.NET 6 预览版4

    我们很高兴发布 .NET 6 Preview 4.我们现在大约完成了 .NET 6 发布的一半.现在是一个很好的时机,可以再次查看.NET6的完整范围.许多功能都接近最终形式,而其他功能将很快推出,因 ...