当前位置:   article > 正文

SHADER学习笔记(一):Surface Shader

surface shader

Surface Shader是Unity为了方便shader编写提供的特殊功能,它对底层的vertex/fragment shader做了封装,省去了一些重复代码编写的工作量。我的理解是它同时具有vertex/fragment shader的功能,只是写法上更加简洁,更容易上手。

Unity的官方manual上就提供了几个最好的学习例子,我在学习的过程中加上了注释。

简单的漫反射(SIMPLE)

这里写图片描述

 

  1. Shader "Example/Diffuse Simple" {
  2. SubShader {
  3. Tags { "RenderType" = "Opaque" } // 标签,决定什么时候渲染(对不透明物体渲染)
  4. CGPROGRAM // //CG语言标记开始
  5. #pragma surface surf Lambert // 编译指令 surface shader 自定义函数 光照模型[1]
  6. struct Input { // 输入的结构体
  7. float4 color : COLOR; // 颜色值
  8. };
  9. void surf (Input IN, inout SurfaceOutput o) { // surface shader处理函数
  10. o.Albedo = 1; // 将基础颜色设为白色[2]
  11. }
  12. ENDCG
  13. }
  14. Fallback "Diffuse" // 发生异常时回滚成Unity内置的Diffuse shader
  15. }

带纹理的漫反射(TEXTURE)

这里写图片描述

 

  1. Shader "Example/Diffuse Texture" {
  2. Properties {
  3. _MainTex ("Texture", 2D) = "white" {} // 纹理,若没有赋值则默认为全白
  4. }
  5. SubShader {
  6. Tags { "RenderType" = "Opaque" }
  7. CGPROGRAM
  8. #pragma surface surf Lambert
  9. struct Input {
  10. float2 uv_MainTex; // 纹理uv坐标
  11. };
  12. sampler2D _MainTex;
  13. void surf (Input IN, inout SurfaceOutput o) {
  14. o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; // 按uv坐标查找纹理上的像素,并获取rgb值
  15. }
  16. ENDCG
  17. }
  18. Fallback "Diffuse"
  19. }

带法向贴图的漫反射(NORMAL MAPPING)

这里写图片描述

 

  1. Shader "Example/Diffuse Bump" {
  2. Properties {
  3. _MainTex ("Texture", 2D) = "white" {}
  4. _BumpMap ("Bumpmap", 2D) = "bump" {} // 法向贴图[3]
  5. }
  6. SubShader {
  7. Tags { "RenderType" = "Opaque" }
  8. CGPROGRAM
  9. #pragma surface surf Lambert
  10. struct Input {
  11. float2 uv_MainTex;
  12. float2 uv_BumpMap;
  13. };
  14. sampler2D _MainTex;
  15. sampler2D _BumpMap;
  16. void surf (Input IN, inout SurfaceOutput o) {
  17. o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
  18. o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap)); // 将从法向贴图中取得的法向量赋给output
  19. }
  20. ENDCG
  21. }
  22. Fallback "Diffuse"
  23. }

边缘光(RIM LIGHTING)

这里写图片描述

 

  1. Shader "Example/Rim" {
  2. Properties {
  3. _MainTex ("Texture", 2D) = "white" {}
  4. _BumpMap ("Bumpmap", 2D) = "bump" {}
  5. _RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0) // 边缘光的颜色
  6. _RimPower ("Rim Power", Range(0.5,8.0)) = 3.0 // 边缘光的强度
  7. }
  8. SubShader {
  9. Tags { "RenderType" = "Opaque" }
  10. CGPROGRAM
  11. #pragma surface surf Lambert
  12. struct Input {
  13. float2 uv_MainTex;
  14. float2 uv_BumpMap;
  15. float3 viewDir; // 观察向量
  16. };
  17. sampler2D _MainTex;
  18. sampler2D _BumpMap;
  19. float4 _RimColor;
  20. float _RimPower;
  21. void surf (Input IN, inout SurfaceOutput o) {
  22. o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
  23. o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
  24. // 对观察向量和法向量求点积,这个值越小,代表这两个方向夹角越接近90度,即为轮廓边缘
  25. half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
  26. // 越接近边缘,发出的光越亮
  27. o.Emission = _RimColor.rgb * pow (rim, _RimPower);
  28. }
  29. ENDCG
  30. }
  31. Fallback "Diffuse"
  32. }

细节纹理(DETAIL TEXTURE)这里写图片描述

 

  1. Shader "Example/Detail" {
  2. Properties {
  3. _MainTex ("Texture", 2D) = "white" {}
  4. _BumpMap ("Bumpmap", 2D) = "bump" {}
  5. _Detail ("Detail", 2D) = "gray" {} // 细节纹理[4]
  6. }
  7. SubShader {
  8. Tags { "RenderType" = "Opaque" }
  9. CGPROGRAM
  10. #pragma surface surf Lambert
  11. struct Input {
  12. float2 uv_MainTex;
  13. float2 uv_BumpMap;
  14. float2 uv_Detail;
  15. };
  16. sampler2D _MainTex;
  17. sampler2D _BumpMap;
  18. sampler2D _Detail;
  19. void surf (Input IN, inout SurfaceOutput o) {
  20. o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
  21. // 将纹理和细节纹理叠加
  22. o.Albedo *= tex2D (_Detail, IN.uv_Detail).rgb * 2;
  23. o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
  24. }
  25. ENDCG
  26. }
  27. Fallback "Diffuse"
  28. }

屏幕空间下的细节纹理(DETAIL TEXTURE IN SCREEN SPACE)

这里写图片描述

 

  1. Shader "Example/ScreenPos" {
  2. Properties {
  3. _MainTex ("Texture", 2D) = "white" {}
  4. _Detail ("Detail", 2D) = "gray" {}
  5. }
  6. SubShader {
  7. Tags { "RenderType" = "Opaque" }
  8. CGPROGRAM
  9. #pragma surface surf Lambert
  10. struct Input {
  11. float2 uv_MainTex;
  12. float4 screenPos; // 屏幕坐标
  13. };
  14. sampler2D _MainTex;
  15. sampler2D _Detail;
  16. void surf (Input IN, inout SurfaceOutput o) {
  17. o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
  18. float2 screenUV = IN.screenPos.xy / IN.screenPos.w;
  19. screenUV *= float2(8,6);
  20. // 根据屏幕的uv坐标来叠加细节纹理[5]
  21. o.Albedo *= tex2D (_Detail, screenUV).rgb * 2;
  22. }
  23. ENDCG
  24. }
  25. Fallback "Diffuse"
  26. }

立方体贴图反射(CUBEMAP REFLECTION)

  1. Shader "Example/WorldRefl" {
  2. Properties {
  3. _MainTex ("Texture", 2D) = "white" {}
  4. _Cube ("Cubemap", CUBE) = "" {} // 立方体贴图[6]
  5. }
  6. SubShader {
  7. Tags { "RenderType" = "Opaque" }
  8. CGPROGRAM
  9. #pragma surface surf Lambert
  10. struct Input {
  11. float2 uv_MainTex;
  12. float3 worldRefl; // 世界反射向量
  13. };
  14. sampler2D _MainTex;
  15. samplerCUBE _Cube;
  16. void surf (Input IN, inout SurfaceOutput o) {
  17. o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb * 0.5;
  18. // 根据世界反射向量和立方体贴图,反射相应的rgb
  19. o.Emission = texCUBE (_Cube, IN.worldRefl).rgb;
  20. }
  21. ENDCG
  22. }
  23. Fallback "Diffuse"
  24. }

世界坐标下的切片(SLICES VIA WORLD SPACE POSITION)

这里写图片描述

 

  1. Shader "Example/Slices" {
  2. Properties {
  3. _MainTex ("Texture", 2D) = "white" {}
  4. _BumpMap ("Bumpmap", 2D) = "bump" {}
  5. }
  6. SubShader {
  7. Tags { "RenderType" = "Opaque" }
  8. Cull Off
  9. CGPROGRAM
  10. #pragma surface surf Lambert
  11. struct Input {
  12. float2 uv_MainTex;
  13. float2 uv_BumpMap;
  14. float3 worldPos;
  15. };
  16. sampler2D _MainTex;
  17. sampler2D _BumpMap;
  18. void surf (Input IN, inout SurfaceOutput o) {
  19. // 自定义的切片函数(将y和z作为参数,意味沿x轴做切片)
  20. clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) - 0.5);
  21. o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
  22. o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
  23. }
  24. ENDCG
  25. }
  26. Fallback "Diffuse"
  27. }

使用顶点修改器的法线挤压(NORMAL EXTRUSION WITH VERTEX MODIFIER)

这里写图片描述

 

  1. Shader "Example/Normal Extrusion" {
  2. Properties {
  3. _MainTex ("Texture", 2D) = "white" {}
  4. _Amount ("Extrusion Amount", Range(-1,1)) = 0.5 // 挤压参数
  5. }
  6. SubShader {
  7. Tags { "RenderType" = "Opaque" }
  8. CGPROGRAM
  9. #pragma surface surf Lambert vertex:vert // 声明会使用顶点修改器
  10. struct Input {
  11. float2 uv_MainTex;
  12. };
  13. float _Amount;
  14. void vert (inout appdata_full v) { // 顶点修改器实现
  15. v.vertex.xyz += v.normal * _Amount; // 沿法线移动顶点的坐标
  16. }
  17. sampler2D _MainTex;
  18. void surf (Input IN, inout SurfaceOutput o) {
  19. o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
  20. }
  21. ENDCG
  22. }
  23. Fallback "Diffuse"
  24. }

逐顶点计算自定义数据(CUSTOM DATA COMPUTED PER-VERTEX)

这里写图片描述

 

  1. Shader "Example/Custom Vertex Data" {
  2. Properties {
  3. _MainTex ("Texture", 2D) = "white" {}
  4. }
  5. SubShader {
  6. Tags { "RenderType" = "Opaque" }
  7. CGPROGRAM
  8. #pragma surface surf Lambert vertex:vert
  9. struct Input {
  10. float2 uv_MainTex;
  11. float3 customColor; // 自定义数据
  12. };
  13. void vert (inout appdata_full v, out Input o) {
  14. UNITY_INITIALIZE_OUTPUT(Input,o);
  15. // 在顶点修改器中,将法向量的绝对值赋值给参数customColor
  16. o.customColor = abs(v.normal);
  17. }
  18. sampler2D _MainTex;
  19. void surf (Input IN, inout SurfaceOutput o) {
  20. o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
  21. // 使用传递过来的参数,对颜色做叠加
  22. o.Albedo *= IN.customColor;
  23. }
  24. ENDCG
  25. }
  26. Fallback "Diffuse"
  27. }

注释:
[1]:Unity中最常用的是两种内置的光照模型:Lambert 和 BlinnPhong
[2]:Albedo是物体的基础颜色,与之不同的Emission是自发光颜色,详细的input和output变量表参见官网
[3]:法向贴图是为了实现凹凸不平的效果,可参见文章
[4]:细节纹理是为了让纹理产生更细腻的感觉,可参见文章
[5]:注意rgb相乘和相加的应用场合不同,这里给出了解释。
[6]:立方体贴图可用来做环境反射和天空盒,可参见文章

 

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

闽ICP备14008679号