表面着色器笔记

来源:互联网 发布:淘宝店铺资质显示51000 编辑:程序博客网 时间:2024/05/29 08:43

第二章 表面着色器和纹理映射

2.1-2.3 部分:

  1. 材质的物理属性在表面函数中进行初始化,存储在“表面输出”的结构内,表面输出(surface output)被传递给光照模型,同时根据光照信息计算出模型上每一个像素最终的颜色。

  2. unity内三种主要的表面输出结构:

    1. SurfaceOutPut
      • fixed3 Albedo 表示材质的漫反射颜色
      • fixed3 Normal 表示切面法线
      • fixed3 Emission 表示材质发射的颜色
      • fixed Alpha 表示材质的透明度
      • half Specular 表示光亮度(0,1)
      • fixed Gloss 表示光强度
    2. SurfaceOutputStandard
      • fixed Albedo 表示材质的基础颜色
      • fixed3 Normal
      • half3 Emission
      • fixed Alpha
      • half Occlusion 表示遮挡,默认为1
      • half Smoothness 表示光滑度,0表示粗糙,1表示光滑
      • half Metallic 0表示非金属,1表示金属
    3. SurfaceOutputStandardSpecular
      • fixed3 Albedo
      • fixed3 Normal
      • half3 Emission
      • fixed Alpha
      • half Occlusion
      • half Smoothness
      • fixed3 Specular 表示高光颜色,可以指定一种颜色而非单一值
  3. Cg中两种类型的变量:单一值变量包装数组。包装数组类似于结构体,一个数组包含数个单一值。
  4. Cg语言内的几种特性

    1. 调和(swizzling)
      o.Albedo=_Color.rgb;
      在本例中,作为fixed3类型的Albedo同时被rgb三个元素所赋值。同时Cg支持对元素重新排序,如_Color.bgr会将红色成分和蓝色成分进行对调。
    2. 涂抹(smearing)
      o.Albedo=0;//Black=(0,0,0);
      将单一值赋给包装数组,单一值将会填充数组的每一元素。
    3. 遮罩(masking)
      o.Albedo.rg=_Color.rg;
      调和用在表达式的左边,以重写包装数组的部分元素。
  5. 包装矩阵: float4*4表示float类型的4行4列的矩阵。通过_mRC标注访问矩阵R行C列的元素。

2-4:给着色器添加纹理

  1. 三维模型由三角形组成,三角形的每一个顶点存储着着色器可以访问的数据。
  2. uv数据:由uv两个坐标组成,取值范围(0,1),表示了++二维图像中像素映射到顶点时的xy坐标++,只是给顶点使用。
    uv数据保存在三维模型中,用建模软件才可编辑。
  3. 工作原理:

    _MainTex("Albedo(RGB)",2D)="White"{}        //实际引用纹理的代码    sampler2D _MainTex        //将纹理定义为二维纹理的标准类型    struct Input{        float2 uv_MainTex;    };        //输入参数,包含三维模型需要渲染的某个特殊点的MainTex的uv值。        fixed4 c=tex2D(_MainTex,In.uv_MainTex)*_Color;        //用来对纹理进行采样,tex2D返回颜色值(包装数组)。```
  4. Filter(过滤)模式:
    • 双线性过滤(Bilinear):廉价有效的平滑纹理方式
    • 点过滤(Point):对于二维游戏,双线性插值可能会产生模糊,用点过滤来移除纹理采样过程中的插值。
  5. Aniso Level:用于减少纹理采样瑕疵。

2-5 通过修改uv值来滑动纹理

  1. 代码

    Shader "Custom/flowShader" {    Properties {        _MainTint("Diffuse Tint",Color)=(1,1,1,1)        _MainTex ("Albedo (RGB)", 2D) = "white" {}        _ScrollXSpeed("X Scroll Speed",Range(0,10))=2    _ScrollYSpeed("Y Scroll Speed",Range(0,10))=2}SubShader {    Tags { "RenderType"="Opaque" }    LOD 200    CGPROGRAM    // Physically based Standard lighting model, and enable shadows on all light types    #pragma surface surf Standard fullforwardshadows    // Use shader model 3.0 target, to get nicer looking lighting    #pragma target 3.0    sampler2D _MainTex;    fixed4 _MainTint;    fixed _ScrollXSpeed;    fixed _ScrollYSpeed;    struct Input {        float2 uv_MainTex;    };    half _Glossiness;    half _Metallic;    fixed4 _Color;    void surf (Input IN, inout SurfaceOutputStandard o) {        fixed2 scrolledUV=IN.uv_MainTex;        fixed xScrollValue=_ScrollXSpeed*_Time;        fixed yScrollValue=_ScrollYSpeed*_Time;        scrolledUV+=fixed2(xScrollValue,yScrollValue);        half4 c=tex2D(_MainTex,scrolledUV);        o.Albedo=c.rgb*_MainTint;        o.Alpha=c.a;        }    ENDCG    }    FallBack "Diffuse"}
  2. 工作原理

    1. 开始时先将UV值保存在scrolledUV的变量中,该变量为float2或fixed2类型。
    2. 用滑动速度变量和內建的递归时间变量_Time来对纹理进行偏移。_Time变量随着时间的推移返回一个递增的浮点数值。 关于递归时间的完整描述:https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html
    3. 计算了不同时刻的偏移之后,将新计算出来的偏移量添加到原始的UV位置(+=运算符的目的),然后将新的UV值作为参数传给tex2D()作为纹理的新UV值。
    4. 实际上是通过操作UV值造成一种纹理在移动的假象。

2-6法线映射

  1. 每一个三维物体都是由三角形的面组成的,每一个三角形都有着其朝向,由三角形中心的法线方向决定,该++中心点++法线决定整个面的朝向。当相隔很近但朝向不同的三角形反射光时,由于朝向不同,着色方向会大不一样,对于弧形表面效果很差。
  2. 因此引入法线方向的概念,在处理弧形表面的光照效果时,使用其法线方向。 在顶点存储的数据中,法线方向是仅次于UV值的一个非常重要的参数。 法线方向是一个单位向量,表示顶点面朝的方向,然而,与面朝方向不同,三角形中的每一个点都有着其独有的法线方向(一个刺猬(基于顶点法线方向线性插值)。
  3. 作用:用一些低精度模型制作高精度的几何体。
  4. 法线映射(bump):通常用一些RGB图像来存贮法线方向(R,G,B)->(X,Y,Z)。unity内使用UnpackNormals()向表面着色器添加法线映射。
  5. 代码
/*null*/

2-7创建透明材质

  1. 在渲染实心物体之前,Unity按照各个物体距离镜头的距离(z排序)对它们进行排列,然后跳过所有没有朝着镜头的三角形(剔除)。
  2. 代码:

    Shader "Custom/Transparent" {    Properties {        _Color ("Color", Color) = (1,1,1,1)        _MainTex ("Albedo (RGB)", 2D) = "white" {}        _Glossiness ("Smoothness", Range(0,1)) = 0.5        _Metallic ("Metallic", Range(0,1)) = 0.0    }SubShader {    Tags {  "Queue"="Transparent"//通道混合的对象(不写入深度缓存的shader)使用该队列,例如玻璃和粒子。    "IgnoreProjector"="True"//确保该物体不受Unity的投影影响    "RenderType"="Transparent"//着色器置换    }    LOD 200    Cull Back//    CGPROGRAM    // Physically based Standard lighting model, and enable shadows on all light types    #pragma surface surf Standard alpha:fade    // Use shader model 3.0 target, to get nicer looking lighting    #pragma target 3.0    sampler2D _MainTex;    struct Input {        float2 uv_MainTex;};    fixed4 _Color;    // #pragma instancing_options assumeuniformscaling    UNITY_INSTANCING_CBUFFER_START(Props)    // put more per-instance properties here    UNITY_INSTANCING_CBUFFER_END    void surf (Input IN, inout SurfaceOutputStandard o) {        float4 c=tex2D(_MainTex,IN.uv_MainTex)*_Color;        o.Albedo=c.rgb;        o.Alpha=c.a;        }        ENDCG    }    FallBack "Diffuse"}
  3. 一些参数

    1. Tags参数详解:https://docs.unity3d.com/Manual/SL-SubShaderTags.html
    2. Unity提供了默认的 渲染序列(Queue):

      渲染队列 渲染队列描述 渲染队列值 Background 这个队列被最先渲染。它被用于skyboxes等。 1000 Geometry 这是默认的渲染队列。它被用于绝大多数对象。不透明几何体使用该队列。 2000 AlphaTest 通道检查的几何体使用该队列。它和Geometry队列不同,对于在所有立体物体绘制后渲染的通道检查的对象,它更有效。 2450 Transparent 该渲染队列在Geometry和AlphaTest队列后被渲染。任何通道混合的(也就是说,那些不写入深度缓存的Shaders)对象使用该队列,例如玻璃和粒子效果。 3000 Overlay 该渲染队列是为覆盖物效果服务的。任何最后被渲染的对象使用该队列,例如镜头光晕。 4000
    3. 事实上透明序列会在几何序列后被渲染,详情搜索:ZBuffing
    4. alpha:fade 这种材质上的每一个像素需要与屏幕上之前的颜色根据其alpha值进行混色。

2-8创建全息着色器

  1. 简介:一种适合于创造未来科技感的着色技术。可以通过噪声,动画扫描线,以及震动来创造特效。
  2. 代码:

    Shader "Custom/Silhouette" {Properties {    _Color ("Color", Color) = (1,1,1,1)    _MainTex ("Albedo (RGB)", 2D) = "white" {}        //用_DotProduct来判断点积多么接近于0时才将三角形视为轮廓。    _DotProduct("Rim effect",Range(-1,1))=0.25}SubShader {    Tags { "RenderType"="Transparent"            "Queue"="Transparent"            "IgnoreProtector"="True"    }    LOD 200    CGPROGRAM    // Physically based Standard lighting model, and enable shadows on all light types    #pragma surface surf Lambert alpha:fade nolighting    //朗伯(Lambertian reflectance)反射,廉价光照模型,nolighting用来禁止光照    sampler2D _MainTex;    struct Input {        float2 uv_MainTex;        float3 worldNormal;        float3 viewDir;    };    fixed4 _Color;    float _DotProduct;    void surf (Input IN, inout SurfaceOutput o) {        // Albedo comes from a texture tinted by color        float4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;        o.Albedo = c.rgb;        float border=1-(abs(dot(IN.viewDir,IN.worldNormal)));        //点积,判断两个向量是否正交(点积为0),根据几何将法线与视线点积为0的三角形视为轮廓        float alpha=(border*(1-_DotProduct)+_DotProduct);        //线性插值,模型的边与_DotProduct之间的渐变褪色,由线性插值完成        o.Alpha = c.a*alpha;        }        ENDCG    }    FallBack "Diffuse"}

2-9 打包和混合纹理

  1. 纹理的作用
    • 存储像素颜色数据
    • 存储x,y方向的像素集合,RGBA通道
    • 将图像打包成一个RGBA纹理,再提取单独的组件作为单独的纹理(节省空间)
    • 将数种纹理混合涂在一个表面上(地形等)
  2. 代码:

    Shader "Custom/battleGround" {Properties {    _MainTint("Diffuse Tint",Color)=(1,1,1,1)    _ColorA("Terrain Color A",Color)=(1,1,1,1)    _ColorB("Terrain Color B",Color)=(1,1,1,1)    _RTexture("Red Channel Texture",2D)=""{}    _GTexture("Green Channel Texture",2D)=""{}    _BTexture("Blue Channel Texture",2D)=""{}    _ATexture("Alpha Channel Texture",2D)=""{}    _BlendTex("Blend Texture",2D)=""{}}SubShader {    Tags { "RenderType"="Opaque" }    LOD 200    CGPROGRAM    // Physically based Standard lighting model, and enable shadows on all light types    #pragma surface surf Lambert    #pragma target 4.0    float4 _MainTint;    float4 _ColorA;    float4 _ColorB;    sampler2D _RTexture;    sampler2D _GTexture;    sampler2D _BTexture;    sampler2D _ATexture;    sampler2D _BlendTex;    struct Input {        float2 uv_RTexture;        float2 uv_GTexture;        float2 uv_BTexture;        float2 uv_Atexture;        float2 uv_BlendTex;};    void surf (Input IN, inout SurfaceOutput o) {        float4 blendData=tex2D(_BlendTex,IN.uv_BlendTex);        float4 rTexData=tex2D(_RTexture,IN.uv_RTexture);        float4 gTexData=tex2D(_GTexture,IN.uv_GTexture);        float4 bTexData=tex2D(_BTexture,IN.uv_BTexture);        float4 aTexData=tex2D(_ATexture,IN.uv_Atexture);        float4 finalColor;    finalColor=lerp(rTexData,gTexData,blendData.g);//r=g    finalColor=lerp(finalColor,bTexData,blendData.b);//g=b                                                                    finalColor=lerp(finalColor,aTexData,blendData.a);//b=a        finalColor.a=1.0;        float4 terrainLayers=lerp(_ColorA,_ColorB,blendData.r);        finalColor *= terrainLayers;        finalColor =saturate(finalColor);        o.Albedo=finalColor.rgb * _MainTint.rgb;        o.Alpha=finalColor.a;    }    ENDCG    }FallBack "Diffuse"}
  3. 工作原理:
    混合纹理通过內建的lerp()函数,将第一个参数和第二个参数按照第三个参数指定的比例进行混合。
    该着色器取纹理的不同纹理进行混合。

2-10 在地形周围创建圆环

  1. 这个案例展示了用着色器画一个圆环,++这个着色器附在的材质将赋给所要画的地形,而不是跟随的物体++。
  2. 代码:
Shader "Custom/RadiusShader" {    Properties {        _MainTex ("Albedo (RGB)", 2D) = "white" {}        _Center("Center",Vector)=(0,0,0,0)//圆心        _Radius("Radius",Float)=0.5//半径        _RadiusColor("Radius color",Color)=(1,0,0,1)        _RadiusWidth("Radius Width",Float)=2    }    SubShader {        Tags { "RenderType"="Opaque" }        LOD 200        CGPROGRAM        // Physically based Standard lighting model, and enable shadows on all light types        #pragma surface surf Standard fullforwardshadows        // Use shader model 3.0 target, to get nicer looking lighting        #pragma target 3.0        float3 _Center;        float _Radius;        fixed4 _RadiusColor;        float _RadiusWidth;        sampler2D _MainTex;        struct Input {            float2 uv_MainTex;            float3 worldPos;//请求当前绘制的像素在世界坐标中的位置        };        void surf (Input IN, inout SurfaceOutputStandard o) {            float d=distance(_Center,IN.worldPos);            //绘制部分,如果在圆环内采用选定颜色,不在的话使用纹理采样的颜色            if(d>_Radius&&d<_Radius+ _RadiusWidth)                o.Albedo=_RadiusColor;            else                o.Albedo=tex2D(_MainTex,IN.uv_MainTex).rgb;        }        ENDCG    }    FallBack "Diffuse"}
//跟随角色的代码,在Update里根据自身position设置在地图绘画的圆心位置。using System.Collections;using System.Collections.Generic;using UnityEngine;public class Radius : MonoBehaviour {    public Material radiusMaterial;    public float radius = 1;    public Color color = Color.white;    // Update is called once per frame    void Update () {        radiusMaterial.SetVector("_Center", transform.position);        radiusMaterial.SetFloat("_Radius", radius);        radiusMaterial.SetColor("_RadiusColor", color);    }}

原创粉丝点击