• 【技术美术实践部分】Unity Shader纹理1.0-使用单张纹理


    之前总结过纹理管线的基础知识

    【技术美术图形部分】纹理基础1.0-纹理管线_flashinggg的博客-CSDN博客

    参考书籍

    Unity Shader 入门精要》冯乐乐,本篇博客对应书的7.1章节内容。


     1 使用单张纹理的Shader完整代码

    与书中不同的是,我的漫反射选择的是半兰伯特。

    1. Shader "Unity Shaders Book/Chapter 7/Single Texture"
    2. {
    3. Properties
    4. {
    5. _Color ("Color Tint", Color) = (1, 1, 1, 1)
    6. _Specular ("Specular", Color) = (1, 1, 1, 1)
    7. _Gloss ("Gloss", Range(8.0, 256)) = 20
    8. _MainTex ("Main Tex", 2D) = "white" {}
    9. }
    10. SubShader
    11. {
    12. Pass {
    13. Tags { "LightMode" = "ForwardBase" }
    14. CGPROGRAM
    15. #pragma vertex vert
    16. #pragma fragment frag
    17. #include "Lighting.cginc"
    18. //properties
    19. fixed4 _Color; //用来控制物体的整体色调!
    20. fixed4 _Specular;
    21. float _Gloss;
    22. sampler2D _MainTex; //用这个纹理来表示漫反射颜色
    23. float4 _MainTex_ST; //固定用法:纹理名称_ST -> 纹理的缩放和平移属性值
    24. struct a2v {
    25. float4 vertex : POSITION;
    26. float3 normal : NORMAL;
    27. float4 texcoord : TEXCOORD0; //将第一组纹理坐标储存在texture里
    28. };
    29. struct v2f {
    30. float4 pos : SV_POSITION;
    31. float3 worldNormal : TEXCOORD0;
    32. float3 worldPos : TEXCOORD1;
    33. float2 uv : TEXCOORD2; //变量uv便于片元着色器进行纹理采样
    34. };
    35. v2f vert(a2v v) {
    36. v2f o;
    37. o.pos = UnityObjectToClipPos(v.vertex);
    38. o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    39. o.worldNormal = UnityObjectToWorldNormal(v.normal);
    40. //变换后的顶点纹理坐标
    41. //_MainTex_ST.xy是缩放,.zw是平移
    42. o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
    43. //o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); //也有Unity内置宏处理纹理变换过程
    44. return o;
    45. }
    46. fixed4 frag(v2f i) : SV_Target {
    47. fixed3 worldNormal = normalize(i.worldNormal);
    48. fixed3 worldlightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
    49. fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
    50. fixed3 halfDir = normalize(viewDir + worldlightDir);
    51. fixed halfLambert = dot(worldNormal, worldlightDir) * 0.5 +0.5;
    52. //albedo - 材质的基础固有色,可以是纹理贴图/纯色的单色
    53. //纹理颜色 和 控制色调的_Color 混合 -> 即相乘
    54. fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
    55. //环境光
    56. //这里的ambient也是经过:light -> 物体 -> 反射出环境光,因此需要用光的颜色✖物体的albedo
    57. fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
    58. //漫反射
    59. //用了半兰伯特
    60. fixed3 diffuse = _LightColor0.rgb * albedo * halfLambert;
    61. //高光
    62. fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(halfDir, worldNormal)), _Gloss);
    63. return fixed4(ambient + diffuse + specular, 1.0);
    64. }
    65. ENDCG
    66. }
    67. }
    68. FallBack "Specular"
    69. }

    Game视图的动态效果

    当然这并不符合常理(指砖块不会有这么大的高光效果),仅作为一个单张纹理应用的参考吧!

    2 纹理相关的shader代码解析

    纹理不同于之前实现简单的光照,会用到更多的知识,在写shader的时候我也有一些疑惑的点,这里就来记录一下。

    2.1 用纹理代替_Color作为漫反射颜色

    Properties中加入了_MainTex:

    纹理来自导入的外部.jpg图片:

    2.2 sampler2D与2D

    Pass中Cg代码中声明了与Properties语义块属性相匹配的变量:

    1. //properties
    2. fixed4 _Color; //用来控制物体的整体色调!
    3. fixed4 _Specular;
    4. float _Gloss;
    5. sampler2D _MainTex; //用这个纹理来表示漫反射颜色
    6. float4 _MainTex_ST; //固定用法:纹理名称_ST -> 纹理的缩放和平移属性值

    其中,_MainTex类型——sampler2D。sampler2DCg变量类型,对应着ShaderLab属性类型2D

    2.3 _MainTex_ST -> 纹理名_ST

    纹理名_ST——即代码中的"_MainTex_ST",是一种Unity的固定方式,以声明纹理的属性,ST是缩放(Scale)和平移(Translation)的缩写。

    2.4 Unity内置宏处理纹理变换

    可以看到顶点着色器代码段中,求传递给片元着色器的顶点纹理坐标是经过缩放、平移变换后的结果,这个结果即可以老老实实用“缩放相乘、平移相加”的步骤:

    o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

    也可以直接用内置宏TRANSFORM_TEX

    o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);

    3 光照相关的shader代码解析

    其实想把这部分重点放在定义的一个变量——albedo上面,其实在代码中我也有注释,尽量解释了每个涉及到albedo代码的意思。

    3.1 albedo与_Color 

    fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;

    3.1.1 _Color的作用是?

    albedo,材质的反射率,你可以理解为它被用来定义物体(也就是SingleTex这个材质)的基础固有色。上述代码段中我理解tex2D(_MainTex, i.uv).rgb是在进行纹理采样获取当前片元的纹理颜色,作为漫反射颜色。但我不理解为什么需要再乘以_Color.rgb值,要知道这个值在之前进行光照模型的时候是当作材质的漫反射颜色(一个单色),按理来说它应该直接被纹理颜色取代了才是。

    这个问题很快就解决了。通过调试发现——留下_Color这个变量是为了控制物体在光照下的整体色调的。 

    3.1.2 类比_Specular的作用?

    也是可以控制高光的色彩,特别是以后面对一些金属,高光会呈现跟基础色不同的颜色。

    3.2 环境光计算与albedo 

    计算漫反射将之前的_Color替换成了albedo都能理解,可为什么计算环境光颜色还需要乘一个albedo?为什么是:

    fixed3 ambient  = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

    而不是直接:

    fixed3 ambient  = UNITY_LIGHTMODEL_AMBIENT.xyz;

    3.2.1 albedo加与不加的效果对比

    不✖albedo:

    ✖albedo:

    好吧......光看这里差别不是很明显,但是还是有一点区别的!那就是“乘以albedo后整体变暗了”。

    现在知道二者在效果上的差别了,那么下一步就是:为什么?

    3.2.2 为什么要✖albedo?

    • 问题1:环境光这一项是为了干什么?

    让我们回顾一下:环境光是为了在只能计算直接光照的标准光照模型中,近似模拟出间接光照的效果(这并不是真正的间接光照哈!),而且整个场景中的物体都会使用同一个环境光,它通常是一个全局变量。这个全局的环境光的颜色和强度信息如何获取?在shader代码中体现的就是UNITY_LIGHTMODEL_AMBIENT这个内置变量。

    • 问题2:光源经历了什么到达人眼?

    这就要涉及到,我们渲染光照目的是模拟人眼看到的颜色效果,而人眼看到的光实际上是经历了:光源发出-->在物体(材质)上进行一部分吸收,再反射-->传递到人眼,这一个过程,而非直接来自光源。

    再结合之前的albedo定义——反射率,所以最终的返回值result中的ambient应该=环境光颜色 * 物体的albedo,这才是最终人眼看到的颜色。

    3.3 高光反射计算与albedo

    3.3.1 不考虑albedo的效果

    书中的例子并没有给高光项specular也✖albedo,那么让我们修改一下代码,书中的效果其实是:

    1. //环境光项
    2. fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
    3. //漫反射项
    4. fixed3 diffuse = _LightColor0.rgb * halfLambert;
    5. //高光项
    6. fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(halfDir, worldNormal)), _Gloss);
    7. //结果
    8. fixed3 resultColor = albedo * (diffuse + ambient ) + specular;
    9. return fixed4(resultColor, 1.0);

    为了与木材粗糙的外观吻合,_Gloss取最小值8,效果如下:

    3.3.2 考虑albedo的效果

    这时代码部分result就要变成:

    fixed3 resultColor = albedo * (diffuse + ambient + specular);

    效果如下:

    明显后者的效果相对更接近木材这种材质,但思考一下,如果换成金属,那毫无疑问就是前者更合适了。

    因此,对于高光项,是否✖材质的基础色,需要结合材质的显示外观效果来综合考虑。

    4 Unity Shader中颜色相加和相乘  

    UnityShader学习(二)像素颜色和颜色向量相加相乘的理解_zsffff的博客-CSDN博客

    可以看看上面这篇文章,总结得很好!

    个人理解:相乘就是颜色需要在进入人眼之前先进行融合,例如环境光计算需要将环境光颜色✖物体基础色;相加就是进入人眼的时刻RGB三通道彼此不相影响,例如漫反射、高光和环境光三者是互不影响的。

  • 相关阅读:
    [附源码]JAVA毕业设计六如文学网站(系统+LW)
    第三十五章 使用 CSP 进行基于标签的开发 - 使用服务器端方法的提示
    Web前端:为什么要学习React?
    JavaScript不会?25分钟带你上手JavaScript ES5-ES6
    《计算广告》第3版读书笔记
    2022.8.13
    kivy报错:OSError: File font_fil not found
    网络安全(黑客)自学
    技战法-信息收集
    springboot230基于Spring Boot在线远程考试系统的设计与实现
  • 原文地址:https://blog.csdn.net/qq_41835314/article/details/127036922