1 前言

固定管线着色器一固定管线着色器二 中介绍了 ShaderLib 的基本用法,本文将接着讲解表面着色器(Surface Shader)的用法。固定管线着色器基于 ShaderLib 命令实现,表面着色器基于 CG 语言实现。目前主流的 Shader 编程语言主要有 GLSL、HLSL、CG,如下:

  • GLSL:OpenGL Shading Language,基于 OpenGL 接口,跨操作系统,依赖硬件,可以在 Windows 、Linux、Mac、Web、移动端跑,这种跨平台是由于 OpenGL 没有提供编译器,由显卡驱动完成着色器的编译工作,即显卡驱动支持 OpenGL,它就可以运行;
  • HLSL:High Level Shading Language,基于 DirectX 接口,由微软研发,不依赖硬件,就算有不同的硬件,同一个着色器编译结果是一样的;
  • CG:C for Graphic,由 NVIDIA(英伟达)研发,跨操作系统,不依赖硬件,它会根据平台的不同编译成相应的中间语言,CG 语言的跨平台性很大程度取决于与微软的合作,这也导致 CG 和 HLSL 非常相似,CG 可以无缝移植到 HLSL 平台上,但是缺点是无法完全发挥 OpenGL 的新特性。

2 CG 语言基础

1)基本数据类型

float // 32位浮点数, 精度: 小数点后6位, 适用: 模型坐标、纹理uv坐标
half // 16位浮点数, 精度: 小数点后3位, 适用: 模型坐标、纹理uv坐标
fixed // 12位浮点数, 精度: 1/256, 适用: 光照计算、颜色
int // 32位整数
bool // 8位布尔数
string // 字符串
sampler1D, sampler2D, sampler3D, samplerCUBE, samplerRECT // 纹理对象

​ 补充:对于 float、half、int、fixed、bool 类型,可以在其后添加 2、3、4,以扩充维度,如:float2、int3、fixed4。

2)Input

struct Input
{
float3 worldPos; // 世界坐标
float3 screenPos; // 屏幕坐标
half3 viewDir; // 观察方向, 顶点指向相机
float4 color : COLOR; // 顶点颜色, 后面的COLOR是语义绑定
fixed2 uv_MainTex; // 纹理uv坐标(命名方式必须是uv_纹理变量名称)
fixed2 uv_NormTex; // 法线纹理uv坐标(命名方式必须是uv_法线纹理变量名称)
};

​ 说明:Input 结构体需要自定义,里面的变量可以根据需要添加。

3)SurfaceOutput

struct SurfaceOutput
{
half3 Albedo; // 反射率, 即颜色纹理rgb
half3 Normal; // 法向量
half3 Emission; // 自发光颜色rgb
half Alpha; // 透明度
half Specular; // 镜面反射度
half Gloss; // 光泽度
};

​ 说明:SurfaceOutput 结构体是系统自定义的结构体,不需要用户定义。

4)常用函数

saturate(var, min, max) // 将变量var约束在min和max之间, 超过边界就取边界值
dot(vec1, vec2) // 向量点乘
cross(vec1, vec2) // 向量叉乘
normalize(vec) // 向量归一化
tex2D(sampler2D, uv_Tex) // 查询纹理坐标对应的纹理值
UnpackNormal(color) // 根据法线纹理解析法线向量

3 光照

​ 在 Assets 窗口右键,依次选择【Create→Shader→Standard Surface Shader】创建 Shader 脚本,实现光照效果代码如下:

​ SurfaceShader.shader

Shader "MyShader/SurfaceShaderTest" {
Properties
{
// 属性名 ("面板显示名称", 类型) = 默认值
_DiffuseColor ("漫反射颜色", Color) = (1, 0, 0, 0.5)
} SubShader
{
CGPROGRAM // CG语言的开始
// 编译指令 着色器类型 函数名称 光照模型 开启透明度
#pragma surface surf Lambert Alpha
// 声明属性变量, 必须与外部属性变量名称一致
fixed4 _DiffuseColor; struct Input
{
float4 color : COLOR; // 顶点颜色, 后面的COLOR是语义绑定
}; void surf (Input IN, inout SurfaceOutput o) // 表面着色器函数
{
o.Albedo = _DiffuseColor.rgb; // 像素颜色
o.Alpha = _DiffuseColor.a; // 像素透明度
} ENDCG // CG语言的结束
} FallBack "Diffuse"
}

​ 创建一个Material,并将 ShaderTest 绑定到该 Material 上,如下:

​ 将该 Material 拖拽到一个 Cube 和 Sphere 游戏对象上。选中绑定的 Material,在 Inspector 窗口调整 Shader 中光照颜色,显示效果如下:

4 贴图

​ SurfaceShader.shader

Shader "MyShader/SurfaceShaderTest" {
Properties
{
// 属性名 ("面板显示名称", 类型) = 默认值
_MainTex ("2阶贴图", 2D) = "white" {}
} SubShader
{
CGPROGRAM // CG语言的开始
// 编译指令 着色器类型 函数名称 光照模型
#pragma surface surf Lambert
// 声明属性变量, 必须与外部属性变量名称一致
sampler2D _MainTex; struct Input
{
fixed2 uv_MainTex; // 纹理uv坐标(命名方式必须是uv_纹理变量名称)
}; void surf (Input IN, inout SurfaceOutput o) // 表面着色器函数
{
o.Albedo = tex2D(_MainTex, IN.uv_MainTex); // 像素颜色
} ENDCG // CG语言的结束
} FallBack "Diffuse"
}

​ 选中绑定的 Material,在 Inspector 窗口选择贴图图片,显示效果如下:

5 法线贴图

​ 光照的漫反射、镜面反射都是通过法线向量计算得到,法线向量经 PackNormal 函数处理后,值域映射到 [0, 1],由于颜色与处理后的法向量维度(3维)和值域([0, 1])一样,因此可以用颜色描述法线向量,即用一张法线图描述原图片每个像素位置的法线,取出法线图中某位置的颜色,再通过 UnpackNormal 函数映射,即可得到对应像素位置的法线向量。

​ 如下,是一张原图和对应的法线图,可以看到,原图和法线图的轮廓很相似,因此美工可以通过调整原图的色相得到法线图。法线的 (x, y, z) 分量对应颜色的 (r, g, b) 分量,z 值越大,法线越偏向观察方向,法线图越偏向蓝色,这也是正视图一般偏向蓝色的原因。Unity3D 中,用户可以通过修改图片的 Texture Type 为 Normal map 生成法线图。

原图与法线图

​ SurfaceShader.shader

Shader "MyShader/SurfaceShaderTest" {
Properties
{
// 属性名 ("面板显示名称", 类型) = 默认值
_MainTex ("2阶贴图", 2D) = "white" {}
_NormTex ("法线贴图", 2D) = "white" {}
} SubShader
{
CGPROGRAM // CG语言的开始
// 编译指令 着色器类型 函数名称 光照模型
#pragma surface surf Lambert // 声明属性变量, 必须与外部属性变量名称一致
sampler2D _MainTex;
sampler2D _NormTex; struct Input
{
fixed2 uv_MainTex; // 纹理uv坐标(命名方式必须是uv_纹理变量名称)
fixed2 uv_NormTex; // 法线纹理uv坐标(命名方式必须是uv_法线纹理变量名称)
}; void surf (Input IN, inout SurfaceOutput o) // 表面着色器函数
{
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb; // 像素颜色
o.Normal = UnpackNormal(tex2D(_NormTex, IN.uv_NormTex)); // 像素法线
} ENDCG // CG语言的结束
} FallBack "Diffuse"
}

​ 选中绑定的 Material,在 Inspector 窗口选择贴图和法线图,并将该 Material 拖拽到一个 Cube 对象上,再新建一个 Cube 对象(作为对比),绑定普通的 Material,显示效果如下:

​ 说明:左边是法线贴图,右边是普通贴图,左边图片明显比右边更有质感。

6 自发光

​ 本节在法线贴图的基础上,在物体凹凸边缘添加高亮效果。物体凹凸边缘是指物体表面法线与观察方向近似垂直的地方。

​ SurfaceShader.shader

Shader "MyShader/SurfaceShaderTest" {
Properties
{
// 属性名 ("面板显示名称", 类型) = 默认值
_MainTex ("2阶贴图", 2D) = "white" {}
_NormTex ("法线贴图", 2D) = "white" {}
_RimColor ("自发光颜色", Color) = (1, 1, 1, 1)
_RimStrength ("自发光强度", Range(0, 5)) = 0.5
} SubShader
{
CGPROGRAM // CG语言的开始
// 编译指令 着色器类型 函数名称 光照模型
#pragma surface surf Lambert // 声明属性变量, 必须与外部属性变量名称一致
sampler2D _MainTex;
sampler2D _NormTex;
fixed4 _RimColor;
half _RimStrength; struct Input
{
fixed2 uv_MainTex; // 纹理uv坐标(命名方式必须是uv_纹理变量名称)
fixed2 uv_NormTex; // 法线纹理uv坐标(命名方式必须是uv_法线纹理变量名称)
half3 viewDir; // 观察方向, 顶点指向相机
}; void surf (Input IN, inout SurfaceOutput o) // 表面着色器函数
{
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb; // 像素颜色
o.Normal = UnpackNormal(tex2D(_NormTex, IN.uv_NormTex)); // 像素法线
half rimPower = 1 - saturate(dot(normalize(IN.viewDir), normalize(o.Normal))); // 计算自发光系数
// o.Emission = _RimColor * _RimStrength * rimPower;
o.Emission = _RimColor * pow(rimPower, _RimStrength); // 自发光颜色
} ENDCG // CG语言的结束
} FallBack "Diffuse"
}

​ 选中绑定的 Material,在 Inspector 窗口选择贴图和法线图,并调整自发光颜色和自发光强度,将该 Material 拖拽到一个 Cube 对象上,显示效果如下:

​ 声明:本文转自【Unity3D】表面着色器

【Unity3D】表面着色器的更多相关文章

  1. Unity3D Shader官方教程翻译(十九)----Shader语法,编写表面着色器

    Writing Surface Shaders Writing shaders that interact with lighting is complex. There are different ...

  2. unity3d游戏开发学习分享之表面着色器讲解

    一.三种着色器的书写格式: 1.surface shaders, 指的是表面着色器 2.vertex and fragment shaders and 指的是顶点和片段着色器 3.fixed func ...

  3. Unity3D着色器Shader编程入门(一)

    自学Unity3D也有大半年了,对Shader一直不敢入坑,最近看了些资料,以及通过自己的实践,对Shader还是有一点了解了,分享下仅作入门参考. 因Shader是对图像图像渲染的,学习前可以去了解 ...

  4. UnityShader 表面着色器简单例程集合

    0.前言 这些简单的shader程序都是写于2015年的暑假.当时实验室空调坏了,30多个人在实验室中挥汗如雨,闷热中学习shader的日子还历历在目.这些文章闲置在我个人博客中,一年将过,师弟也到了 ...

  5. 编写Unity3D着色器的三种方式

    不管你会不会写Unity3D的shader,估计你会知道,Unity3D编写shader有三种方式,这篇东西主要就是说一下这三种东西有什么区别,和大概是怎样用的. 先来列一下这三种方式: fixed ...

  6. Unity表面着色器

    表面着色器和之前无光照着色器不同,其中没有顶点着色器和片元着色器,而增加了光照函数: 接下写了一个求两个贴图的光照效果 两个贴图做插值运算: Shader "Custom/SurfaceSh ...

  7. 关于Unity中表面着色器的使用

    写shader其实就是在两个工位顶点shader工位和着色shader工位插入代码,供GPU使用运行 表面着色器四个函数的入口 1:表面着色器包括4个函数: (1): 顶点变换函数; (2): 表面着 ...

  8. Surface Shader(表面着色器)

    Shader "Custom/Surface_Shadeer" { Properties {                                             ...

  9. Unity Shader入门精要学习笔记 - 第17章 Unity的表面着色器探秘

    转自 冯乐乐的<Unity Shader 入门精要> 2010年的Unity 3 中,Surface Shader 出现了. 表面着色器的一个例子. 我们先做如下准备工作. 1)新建一个场 ...

  10. Unity3D碰撞器事件测试(Rigidbody/Kinematic/Trigger/Collider)

    1.Kinematic和刚体之间的碰撞事件 Unity官方有一个详细的碰撞关系表:http://docs.unity3d.com/Manual/CollidersOverview.html 但其实可以 ...

随机推荐

  1. idea 修改 jsp文件之后不生效问题

  2. 配置Chrome支持网页内的frame跨域

    前言 跨域限制可以保证安全,但是调试的时候关掉会更方便,然而现在网络上能找到的关闭跨域限制方法,在新版的Chrome浏览器上根本没用-- 经过一番摸索,发现用旧版的Chrome就可以绕过跨域限制,刚好 ...

  3. [转帖]Nginx 安全优化

    目录 前言 1.使用 SSL/TLS 证书 2.使用安全密钥交换机制 3.禁用旧的 SSL/TLS 协议 4.禁用 SSL/TLS 弱密码套件 5.禁用不需要的 HTTP 方法 6.防止缓冲区溢出攻击 ...

  4. [转帖]Nginx反向代理中使用proxy_redirect重定向url

    https://www.cnblogs.com/kevingrace/p/8073646.html 在使用Nginx做反向代理功能时,有时会出现重定向的url不是我们想要的url,这时候就可以使用pr ...

  5. [转帖]HTTP2 Sampler for JMeter

    https://www.cnblogs.com/a00ium/p/10462572.html 今天开发大大说能不能帮忙压一下HTTP2的链接,便去查了一下相关的东西. HTTP 2.0 的出现,相比于 ...

  6. [转帖]jmeter线程组与循环次数的区别

    在压测的时候,有些接口需要携带登录信息,但是我们只想登录一次,然后其他接口进行多用户压测,此时你会怎么办?用仅一次控制器实现吗?下面我们来看看用仅一次控制器能不能实现 压测时jmeter中的线程数是模 ...

  7. [转帖]egrep及扩展的正则表达式

    egrep: egrep = grep -E 语法:egrep [选项] PATTERN [FILE...] 扩展正则表达式的元字符: 字符匹配: .:匹配任意单个字符: [ ]:匹配指定范围内的任意 ...

  8. [转帖]UTF8 和 AL32UTF8 的区别

    本文章向大家介绍UTF8 和 AL32UTF8 的区别,主要内容包括 .使用实例.应用技巧.基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下.  UTF8 和 AL32UTF8 ...

  9. Mixed spaces and tabs

    ykit打包过程中报错信息如下: 报错原因: Mixed spaces and tabs(混合空格和制表符). 大多数代码约定要求使用空格或 tab 进行缩进,因此,一行代码若同时混有 tab缩进和空 ...

  10. 文档级关系抽取:基于结构先验产生注意力偏差SSAN模型

    文档级关系抽取:基于结构先验产生注意力偏差SSAN模型 Entity Structure Within and Throughout: Modeling Mention Dependencies fo ...