zoukankan      html  css  js  c++  java
  • Unity3D学习笔记3——Unity Shader的初步使用

    1. 概述

    在上一篇文章《Unity3D学习笔记2——绘制一个带纹理的面》中介绍了如何绘制一个带纹理材质的面,并且通过调整光照,使得材质生效(变亮)。不过,上篇文章隐藏了一个很重要的细节——Unity Shader。Shader(着色器)是渲染管线中可被用户编程的阶段,依靠着色器可以控制渲染管线的细节。现代图像渲染技术,都把Shader封装成与Material(材质)相关的组件。所以这篇文章,我们就初步学习下在Unity中使用Shader。

    2. 详论

    2.1. 创建材质

    在上一章中,材质、以及材质相关的资源是在Unity3D编辑器中创建,在C#脚本中直接引用的。这里为了学习使用Shader,我们使用自定义的Shader,可以在C#脚本中创建材质。修改上一章代码的材质部分:

    Shader shader = Shader.Find("Custom/MainShader");
    Material material = new Material(shader);
            
    Texture2D texture = Resources.Load<Texture2D>("ImageDemo");
    material.mainTexture = texture;
    
    MeshRenderer meshRenderer = newGameObject.AddComponent<MeshRenderer>();      
    meshRenderer.material = material;
    

    可以看到,要创建一个Material,首先得创建一个Shader。我们在Project视图中右键菜单->Create->Standard Surface Shader,创建一个标准表面着色器MainShader:

    imagelink1

    双击打开这个Shader,可以看到这个Shader的具体内容。标准着色器很复杂,我们清空里面的内容,填入我们这个更简单的着色器示例:

    Shader "Custom/MainShader"
    {
        Properties
        {       
            _MainTex ("Texture", 2D) = "white" {}      
        }
        SubShader
        {
            Tags{"Queue" = "Geometry"}
    		
    		Cull Back
    
    		Pass
    		{
    		    CGPROGRAM
        
    			#pragma vertex vert	
    			#pragma fragment frag
    
    			sampler2D _MainTex;
    
    			//顶点着色器输入
    			struct a2v
    			{
    				float4  position : POSITION;
    				float3  normal: NORMAL;
    				float2  texcoord : TEXCOORD0;	
     			};
    
    			//顶点着色器输出
    			struct v2f
    			{
    				float4 position: SV_POSITION;
    				float2 texcoord: TEXCOORD0;
    			};
    
    			v2f vert(a2v v)
    			{
    				v2f o;	
    				o.position = mul(UNITY_MATRIX_MVP, v.position);									
    				o.texcoord = v.texcoord;
    
    				return o;
    			}
    
    			fixed4 frag(v2f i) : SV_Target 
    			{
    				return tex2D(_MainTex, i.texcoord);			
    			}
       
    			ENDCG
    		}
        }
        FallBack "Diffuse"
    }
    

    2.2. 着色器

    Unity使用的着色器语言叫做ShaderLab,它是图形渲染中Shader(例如GLSL,HLSL以及CG)的更高级更抽象一级的封装。ShaderLab是个非常简单的说明性描述语言,通过嵌套在花括号中的语义来描述Unity Shader文件。

    2.2.1. 名称

    通过Shader语义指定Unity Shader的名称:

    Shader "Custom/MainShader"
    {
    
    }
    

    这个名称非常重要,在Unity编辑器中需要通过这个名字来引用Shader。

    2.2.2. 属性

    Shader语义块的第一个语义块是Properties语义块,它连接着材质和Unity3d编辑器,设置了这个属性就能够通过材质面板调整材质,调整材质的本质就是调整Shader。Properties的定义通常描述如下:

    Properties {
    	Name ("display name",PropertyType) = DefaultValue
    }
    

    Name指的是在Shader中使用的名称,display name指的是显示在材质面板的名称。PropertyType则有点容易混淆,它指的是显示在材质面板中的属性类型,借用一下《Unity Shader入门精要》的图表:

    imagelink2

    2.2.3. SubShader

    每个Unity Shader都至少包含一个SubShader语义块,Unity会优先选择第一个能够在当前平台下运行的SubShader作为最终渲染效果的Shader。

    这个语义块下面又会包含三个语义块:

    2.2.3.1. 标签(Tags)

    SubShader的标签用于用于标识何时以何种方式被渲染到渲染引擎,它由一系列键值对组成。Queue是最常用的标签,用于标识渲染物体在渲染队列中的位置:
    imagelink3

    我们这里,把这个渲染物体放到Geometry队列中,这个位置通常放置不透明物体的渲染:

    Tags{"Queue" = "Geometry"}
    

    2.2.3.2. 渲染状态(RenderSetup)

    渲染状态用于设置图形硬件的各种状态,例如是否应开启 Alpha 混合或是否应使用深度测试等。在像OpenGL这样的图形接口中,通常是以函数的形式进行调用的,Unity3d将其放在Shader里面,也有一定的道理。

    这里的渲染状态设置成将背面裁剪掉:

    Cull Back
    

    2.2.3.3. 通道(Pass)

    在Pass语义块中,才是像OpenGL/DirectX中使用的Shader。OpenGL使用的着色器语言叫做GLSL,DirectX使用的着色器语言叫做HLSL,Unity3D则推荐使用Cg语言,这是一种类C语言,与HLSL非常相似。Cg语言代码段在Pass语义块中被包裹在CGPROGRAM和ENDCG之间:

    CGPROGRAM
    //...
    ENDCG
    

    2.2.4. 回退(FallBack)

    FallBack定义了一种退化策略,由于不同机器支持的性能特性不同,如果之前的子着色器都不生效,那么就使用这个着色器,通常这个着色器是内置的:

    FallBack "Diffuse"
    

    2.3. 渲染管线

    图形渲染引擎的渲染管线其实是个内涵非常丰富的概念,再次借用《Unity Shader入门精要》的插图,渲染管线的描述大致如下:

    imagelink4

    当然只看这个图是不够的,但是我们可以直接从代码层面去了解它。镶嵌在CGPROGRAM和ENDCG之间的CG代码,体现的正是渲染管线的思维。

    首先,通过编译指令,分别指定顶点着色器程序和片元着色器程序:

    #pragma vertex vert	
    #pragma fragment frag
    

    vert就是顶点着色器的函数,在这个着色器程序中指定了计算了顶点坐标和纹理坐标:

    v2f vert(a2v v)
    {
    	v2f o;	
    	o.position = mul(UNITY_MATRIX_MVP, v.position);									
    	o.texcoord = v.texcoord;
    
    	return o;
    }
    

    传入参数是一个结构体,POSITION,NORMAL,TEXCOORD0是Unity Shader中固定的语义,分别代表这位置、法向量以及纹理坐标,他们也被称为顶点属性。还记得在上一篇文章《Unity3D学习笔记2——绘制一个带纹理的面》中创建Mesh时给Mesh创建的成员变量vertices、uv和normals吧?给他们传入的数据正是在这里用到了。

    //顶点着色器输入
    struct a2v
    {
    	float4  position : POSITION;
    	float3  normal: NORMAL;
    	float2  texcoord : TEXCOORD0;	
    };
    

    传出参数则是另外一个结构体:

    //顶点着色器输出
    struct v2f
    {
    	float4 position: SV_POSITION;
    	float2 texcoord: TEXCOORD0;
    };
    

    SV_POSITION表示的是裁剪空间坐标,也就是在顶点着色器中计算的顶点值。这个计算内容的内涵也挺丰富的,简单来说,创建Mesh时的顶点坐标,经过一个模型变换(Model)、视图变换(View)、投影变换(Projection),最终变成了裁剪空间坐标系中的坐标,体现在着色器中,就是内置的MVP矩阵UNITY_MATRIX_MVP。

    剩下的就是片元着色器函数的部分了。在这个着色器中,_MainTex也就是我们先前创建的,并且传递到材质中的纹理,通过将顶点着色器中传递过来的纹理坐标进行采样,得到具体的片元颜色:

    sampler2D _MainTex;
    
    fixed4 frag(v2f i) : SV_Target 
    {
    	return tex2D(_MainTex, i.texcoord);			
    }
    

    最终显示的效果如下:
    imagelink5

    可以看到这里显示的就是图片本身的颜色,这是因为在着色器中只是采样了图片的颜色,并没有光照计算的参与。也就是在图形引擎中,任何效果的设置只是表象,任何效果的实现都会归结到着色器中。

  • 相关阅读:
    hadoop 2.7 添加或删除datanode节点
    hadoop HA集群的安装
    Linux-PAM认证
    [Notice]博客地址转移 vitostack.com
    WPF 自定义 MessageBox (相对完善版 v1.0.0.6)
    python2.7 倒计时
    Hadoop Streaming框架使用(二)
    Hadoop错误码速查
    Hadoop Streaming框架使用(一)
    python清空列表的方法
  • 原文地址:https://www.cnblogs.com/charlee44/p/15087159.html
Copyright © 2011-2022 走看看