当前位置:   article > 正文

【Unity3D】表面着色器_unity着色器

unity着色器

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)基本数据类型

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

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

        2)Input

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

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

        3)SurfaceOutput

  1. struct SurfaceOutput
  2. {
  3. half3 Albedo; // 反射率, 即颜色纹理rgb
  4. half3 Normal; // 法向量
  5. half3 Emission; // 自发光颜色rgb
  6. half Alpha; // 透明度
  7. half Specular; // 镜面反射度
  8. half Gloss; // 光泽度
  9. };

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

        4)常用函数

  1. // 数值计算
  2. sign(x)、abs(x) // 符号、绝对值
  3. min(a, b)、max(a, b) // 最值函数
  4. ceil(x)、floor(x)、round(x) // 取整函数
  5. frac(x) // 取小数部分
  6. fmod(x, y) // 取余数
  7. rap(x) // 倒数(1/x)
  8. sqrt(x)、pow(x) // 幂函数
  9. exp(x)、exp2(x) // 指数函数(e^x、2^x)
  10. log(x)、log10(x)、log2(x) // 对数函数
  11. degrees(x)、radians(x) // 角度转换函数
  12. sin(x)、cos(x)、tan(x)、asin(x)、acos(x)、atan(x) // 三角函数
  13. sinh(x)、cosh(x)、tanh(x) // 双曲线函数
  14. saturate(x) // 将x约束在0和1之间, 超过边界就取边界值
  15. clamp(x, min, max) // 将x约束在min和max之间, 超过边界就取边界值
  16. smoothstep (min, max, x) // 平滑比例, 公式: k=saturate((x-min)/(max-min)), y=k*k*(3-2*k)
  17. lerp(a, b, f) // 插值, 公式: y=x+f*(y-x), a、b可以是向量
  18. // 向量计算
  19. distance(pos1, pos2) // 计算pos1与pos2之间的距离
  20. length(vec) // 计算向量的模长
  21. normalize(vec) // 计算向量的单位向量
  22. dot(vec1, vec2) // 向量点乘
  23. cross(vec1, vec2) // 向量叉乘
  24. reflect(i, n) // 根据入射向量和法线向量计算反射向量
  25. // 矩阵计算
  26. mul(M, N)、mul(M, v), mul(v, M) // M*N、M*v、v*M
  27. // 纹理计算
  28. tex2D(sampler2D, uv_Tex) // 查询纹理坐标对应的纹理值
  29. UnpackNormal(color) // 根据法线纹理解析法线向量

3 光照

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

        SurfaceShader.shader

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

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

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

4 贴图

        SurfaceShader.shader 

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

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

5 法线贴图

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

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

原图与法线图

         SurfaceShader.shader

  1. Shader "MyShader/SurfaceShaderTest" {
  2. Properties
  3. {
  4. // 属性名 ("面板显示名称", 类型) = 默认值
  5. _MainTex ("2阶贴图", 2D) = "white" {}
  6. _NormTex ("法线贴图", 2D) = "white" {}
  7. }
  8. SubShader
  9. {
  10. CGPROGRAM // CG语言的开始
  11. // 编译指令 着色器类型 函数名称 光照模型
  12. #pragma surface surf Lambert
  13. // 声明属性变量, 必须与外部属性变量名称一致
  14. sampler2D _MainTex;
  15. sampler2D _NormTex;
  16. struct Input
  17. {
  18. fixed2 uv_MainTex; // 纹理uv坐标(命名方式必须是uv_纹理变量名称)
  19. fixed2 uv_NormTex; // 法线纹理uv坐标(命名方式必须是uv_法线纹理变量名称)
  20. };
  21. void surf (Input IN, inout SurfaceOutput o) // 表面着色器函数
  22. {
  23. o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb; // 像素颜色
  24. o.Normal = UnpackNormal(tex2D(_NormTex, IN.uv_NormTex)); // 像素法线
  25. }
  26. ENDCG // CG语言的结束
  27. }
  28. FallBack "Diffuse"
  29. }

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

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

6 自发光

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

         SurfaceShader.shader

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/107833
推荐阅读
相关标签
  

闽ICP备14008679号