开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:1667

当前位置:游戏蛮牛 技术专区 开发者专栏

__________________________________________________________________________________
开发者干货区版块规则:

  1、文章必须是图文形式。(至少2幅图)
      2、文章字数必须保持在1500字节以上。(编辑器右下角有字数检查)
      3、本版块只支持在游戏蛮牛原创首发,不支持转载。
      4、本版块回复不得无意义,如:顶、呵呵、不错......【真的会扣分的哦】
      5、......
__________________________________________________________________________________
查看: 478|回复: 2

[哪来的胖子] ShaderLab 第一节 ShaderLab的基本框架

[复制链接]  [移动端链接]
排名
2087
昨日变化
1

33

主题

101

帖子

1266

积分

Rank: 9Rank: 9Rank: 9

UID
35368
好友
19
蛮牛币
2630
威望
0
注册时间
2014-7-20
在线时间
502 小时
最后登录
2017-4-24

专栏作家活力之星

QQ
发表于 2016-11-13 11:34:35 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本帖最后由 哪来的胖子 于 2016-12-5 21:33 编辑

内容说明:
    1.了解shaderlab的结构
    2.shaderlab支持的编程语言
    3.unity中shader的三种形态
    4.shader的数据接口:属性和uniform变量


1.了解shaderlab的结构
         作为一款跨平台的游戏开发引擎,对于要适应不同GPU的shader来说,unity使用自定义的ShaderLab来组织Shader的内容,并针对不同平台进行进行编译。
1.1 ShaderLab的结构
         Unity的Shader文件是通过Shader关键字开始的,可以向目录一样组织shader的命名。Shader的文件名和引用名不必一样,但是开发时建议由一定的规则,方便超找和修改。
[C#] 纯文本查看 复制代码
Shader "Test/Demo001"
{
        Properties
        {
                _MainTex ("Texture", 2D) = "white" {}
        }
        SubShader
        {
        //...
        }
        SubShader
        {
        //...
        }
        FallBack "Diffuse"
}


1.2 使用subshader来组织shader的不同实现
         真正的渲染物体的内容实在SubShader中实现的。之所以有很多SubShader代码块,是为了实现不同的显卡使用不同的shader,尽可能的完美的展现效果。Unity会根据实际的运行环境,从上到下的选出最优秀的SubShader来实现效果(看起来很牛,也别太信)。理论上对SubShader的数量没有限制,但实际开发中一般只写两到三种,一种针对老式显卡,一种针对当前主流的显卡。


1.3 SubShader的标签
         SubShader的Tags{}可以设定标签,告诉引擎或者用户如何认证这个SubShader。
[C#] 纯文本查看 复制代码
SubShader
{
        Tags { "Queue"="Geometry" "RenderType"="Opaque" "IgnoreProjector"="true" }
//…
}

Queue就是渲染队列。表示unity在什么时候渲染自己。Queue有5个值可以选择,它们是Background、eometry、lphaTest、ransparent和Overlay,他们对应的数字分别是1000、2000、2450、3000和4000。既然是数字,方然也可以使用加减来表示,也可以使用纯数字表示。
值越大渲染的次数越靠后。

RenderType标签,其常用内置值有 Opaque、Transparent、TransparentCutout、Backgroud
和Overlay。这个标签在替换渲染做PostEffect是很重要,也有其他一些地方使用,这里先不做详细讲解。

IgnoreProjector 这个就比较简单了,就是忽略投影。
想了解投影的可以看下投影器的介绍(http://www.ceeger.com/Components/class-Projector.html),一般人物是接受投影的,场景(使用LightMap)是不接受投影的,主要是叠加起来比较怪(个人感觉)。

SubShader自定义标签,格式如下:
[C#] 纯文本查看 复制代码
Tags { "MyTag" = "TagValue"}

这种做法一般会在替换渲染中用到。


替换渲染的说明(http://www.ceeger.com/Components/SL-ShaderReplacement.html)

1.4 SubShader中的pass块
         SubShader包装了一个渲染方案,而这个方案由一个个的pass快来执行。SubShader可以包括多个pass块,每一个pass快都包含了渲染一个几何体的具体代码。


1.5pass块中的标签说明
         Pass块中的标签都是针对渲染路径的,告诉引擎应该这个pass在什么渲染路径下渲染。这里不对pass的标签做具体讲解,下面有用到的会进行简单说明。以后会专门讲解的。
         Pass的Name是用来引用改pass块的。
[C#] 纯文本查看 复制代码
Shader "Test/Demo001"
{
        Properties
        {
                _MainTex ("Texture", 2D) = "white" {}
        }
        SubShader
        {
                Pass
                {
                        Name "MyRealPass"
                }
        }
}

在另外一个shader中调用
[C#] 纯文本查看 复制代码
Shader "Test/Demo002"
{
        SubShader
        {
                UsePass "Test/Demo001/MyRealPass"
        }
}


1.6  FallBack保证shader的广泛性
         如果所有的SubShader都失败了,为了用户在计算机上面的显示会使用FallBack指定的shader来渲染,最好是使用untiy自己预制的shader实现,这样可以确保平台最大的适应性。

2.0  shaderlab所支持的shader编程语言
         Untiy的shaderlab支持GLSL写的shader,也能适应Cg/HLSL来写。用GLSL来写的代码必须在GLSLPROGRAM和ENDGLSL关键字之间。用CG/HGLS来写的话必须在CGPROGRAM和ENDCG之间。关于CG的相关细心可以百度下,是NVIDIA公司开发的。

3.0 unity中shader的三种形态
3.1固定管线
         固定管线是老一代GPU能力比较有限的时,对shader的约束性比较高的一种形态。对应OpenGL的1.0API,不能进行编程设置,一些开关。如果你对OpenGL1.0还不了解,可以查看一下这个链接(http://www.unitybeginner.com/blog/?tag=opengl)
         下面是一个固定管线的例子:(如果你看过前面的OpenGL1.0教程,这些代码应该很容易理解)
[C#] 纯文本查看 复制代码
Shader "Lession01/Demo003"
{
         Properties {
        _Color ("Main Color", Color) = (1,1,1,0.5)
        _SpecColor ("Spec Color", Color) = (1,1,1,1)
        _Emission ("Emmisive Color", Color) = (0,0,0,0)
        _Shininess ("Shininess", Range (0.01, 1)) = 0.7
        _MainTex ("Base (RGB)", 2D) = "white" { }
    }
    SubShader {
        pass {
            Material {
                Diffuse [_Color]
                Ambient [_Color]
                Shininess [_Shininess]
                Specular [_SpecColor]
                Emission [_Emission]
            }
            Lighting On//顶点光照
            SeparateSpecular On//开启独立镜面反射
            SetTexture [_MainTex] {
                constantColor [_Color]
                Combine texture * primary DOUBLE, texture * constant
            }
        }//
    }
}

OpenGL1.0的实现可以查看这里http://www.unitybeginner.com/blog/?p=59


3.2可编程shder
         如果你想自己处理光照,可以写vertex+fragment sahder,相当于OpenGL3.0。如果你对OpenGL3.0不是很了解可以在这里了解(http://www.unitybeginner.com/blog/?tag=opengl3-0)。
下面是一个可编程Shader的一个例子:(如果你看过前面的OpenGL3.0教程,这些代码应该很容易看懂)
[C#] 纯文本查看 复制代码
Shader "Lession01/Demo004" {
        Properties {
                _MyTexture ("Texture (RGB)", 2D) = "white" {}
                _MyColor("Color of Object",Color)=(1,1,1,1)
        }
        SubShader {
        Tags{"Queue"="Geometry" "RenderType"="Opaque" "IgnoreProjector"="True"}
        pass{
        CGPROGRAM
        #pragma vertex vert//顶点着色器
        #pragma fragment frag//片段着色器
        #include "UnityCG.cginc"

        sampler2D _MyTexture;
        float4 _MyColor;

        struct v2f{
                float4 pos:SV_POSITION;
        };
        v2f vert(appdata_full v)
        {
                v2f o;
                o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
                return o;
        }
        float4 frag(v2f i):COLOR
        {
                return float4(1,1,1,1);
        }
        ENDCG
                }
        } 
        FallBack "Diffuse"
}


3.3 ShaderLab的骄傲(官方是这么说的):SurfaceShader
         如果你想写一个能处理不同的照明、点光源、平行光、光照贴图等。又能够处理不同的阴影选项,还能同时在untiy的两个渲染路径(Forward和 Deferred)下正常工作,是一件很复杂、很烦人的事情。Unity通过Surface Shader把上面的这一切包装起来了。下面就是一个例子:
[C#] 纯文本查看 复制代码
Shader "Lession01/Demo005" {
        Properties {
                _MainTex ("Base (RGB)", 2D) = "white" {}
        }
        SubShader {
                Tags { "RenderType"="Opaque" }
                LOD 200
                
                CGPROGRAM
                #pragma surface surf Lambert

                sampler2D _MainTex;

                struct Input {
                        float2 uv_MainTex;
                };

                void surf (Input IN, inout SurfaceOutput o) {
                        half4 c = tex2D (_MainTex, IN.uv_MainTex);
                        o.Albedo = c.rgb;
                        o.Alpha = c.a;
                }
                ENDCG
        } 
        FallBack "Diffuse"
}

Surface Shader最终会被编译成一个复杂的vertex+fragmentShader。


4.0 Shader的数据接口:属性和uninform变量

4.1 uninform说明:(OpenGL3.0的教程中使用过,没有进行过过多的解释)
         Uniform是变量类型的一种修饰符,是OpenGL中被着色器中的常量值,使用存储各种着色器需要的数据,例如:转换矩阵、光照参数或者颜色。
uniform 的空间被顶点着色器和片段着色器分享。

4.2在Properties中定义属性:
         格式:name (“display name“, Type) = DefaultValue
[C#] 纯文本查看 复制代码
Properties {
                _MyTexture ("Texture (RGB)", 2D) = "white" {}//纹理图片类的属性
                _MyColor("Color Show Name",Color)=(1,1,1,1)//颜色
                _MyCube("Cube Show Name" ,Cube) = "white"{}//3d贴图,需要6张
                _MyVector("Vector Show Name",vector) = (1,1,1,1)//四个元素的向量
                _MyFloat("Float Show Name",float) = 1.0//浮点数
                _MyRang("Range Show Name",range(-1.0,1.0)) = 0;//限定范围浮点数
        }

4.3 通过界面操作属性:
效果如如下:
01属性面板说明.jpg
4.3 通过脚本控制属性
         除了图形界面,我们还可以通过脚本来读取和写入我们定义的属性。例子如下:
[C#] 纯文本查看 复制代码
using UnityEngine;
using System.Collections;

public class Lession01 : MonoBehaviour {

    public Material mat;
    public Texture myPic;
    public Color color;
    public Cubemap cube;
    public Vector4 vec;
    public float val1;
    public float val2;

        void Update () {
            //对shader中的属性进行设置
        mat.SetTexture("_MyTexture", myPic);
        mat.SetColor("_MyColor", color);
        mat.SetTexture("_MyCube", cube);
        mat.SetVector("_MyVector", vec);
        mat.SetFloat("_MyFloat", val1);
        mat.SetFloat("_MyRang", val2);

        //对shader中的定义属性进行读取
        myPic = mat.GetTexture("_MyTexture");
        color = mat.GetColor("_MyColor");
        cube = (Cubemap)mat.GetTexture("_MyCube");
        vec = mat.GetVector("_MyVector");
        val1 = mat.GetFloat("_MyFloat");
        val2 = mat.GetFloat("_MyRang");
        }
}

01属性面板说明02.jpg
4.5 矩阵:不能再属性块中定义
       我们必须现在shader中声明,然后通过脚本来进行读写。
         Shader中的声明:
[C#] 纯文本查看 复制代码
Uniform float4x4 myMatrix;

  在代码中的读取
[C#] 纯文本查看 复制代码
//矩阵读取
matrix4x4 = mat.GetMatrix("myMatrix ");
mat.SetMatrix("myMatrix ", matrix4x4);

4.6 在shader中使用属性
         在Properties块中定义的属性必须在代码中再次声明一次才能被使用。在cg声明方式如下:
[C#] 纯文本查看 复制代码
sampler2D _MyTexture;
float4 _MyColor;
samplerCUBE _MyCube;
float4 _MyVector;
float _MyFloat;
float _MyRang;
uniformfloat4x4 myMatrix;


相关资源下载地址:http://pan.baidu.com/s/1c2C8KJe

更多教程欢迎访问随风工作室博客:http://www.unitybeginner.com

评分

参与人数 1鲜花 +5 收起 理由
浮云zzy + 5 最近正在学习这块

查看全部评分


回复

使用道具 举报

5熟悉之中
503/1000
排名
4597
昨日变化
3

0

主题

178

帖子

503

积分

Rank: 5Rank: 5

UID
180505
好友
1
蛮牛币
68
威望
0
注册时间
2016-11-3
在线时间
115 小时
最后登录
2017-4-24
QQ
发表于 2016-11-13 15:45:48 | 显示全部楼层
这两天一直在看视频学ShaderLab,这里巩固一下,感谢楼主分享

回复 支持 反对

使用道具 举报

5熟悉之中
503/1000
排名
4705
昨日变化

5

主题

152

帖子

503

积分

Rank: 5Rank: 5

UID
158249
好友
1
蛮牛币
1299
威望
0
注册时间
2016-7-22
在线时间
144 小时
最后登录
2017-4-24
发表于 2016-11-22 16:12:41 | 显示全部楼层
在复习复习

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表