第一个简单的顶点vert/片元frag着色器

     1)打开Unity 5.6编辑器,新建一个场景后ctrl+s保存命名为Scene_5。默认创建的场景是包含了一摄像机,一平行光,且场景背景是一天空盒而非纯色。在这里菜单中选择 Window->lighting->settings,会弹出一个光照选项设置框如下图:

     

     点击箭头处选择“None”资源即可去掉天空盒,看到一个纯色背景。

     2)右键create一C# script,命名为shader_5,放置脚本到shader文件夹。(相当于定义了如何“穿衣服”)

     3)右键新建shader/Standard Surface Shader,命名为shaderMat_5,放置到Material文件夹。(相当于定了一“外表衣服”) 然后把2)中的shader文件赋给它(注意:这里我们的shader文件全部都放到shader文件夹统一管理,那每个shader代码编写命名要“shader/xxx”,如下图)。

  

  

     4)右键create一个Capsule柱体,把第3)的材质拖到这个Sphere球体,渲染这个柱体的时候就能运行自定义的shader代码。(相当于告诉对象,按照shader定义的‘穿衣方式’给对象穿上材质衣服)

     5)使用standard shader原有默认代码,渲染的效果如下:

    

     复制粘贴以下的代码到shader文件,

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "shader/shader_5" {
SubShader {
Pass {
CGPROGRAM #pragma vertex vert
#pragma fragment frag float4 vert(float4 v : POSITION) : SV_POSITION {
return UnityObjectToClipPos(v);
} fixed4 frag() : SV_Target {
return fixed4(1.0, , , );
} ENDCG
}
}
}

     运行看到的效果如下:

    

     下面详细分析,代码第一行通过“../文件夹名/shader文件名”形式定义了每个u shader的名字,有利于为材质球选择shader时右键快速找到自定义的shader文件(“/”控制shader文件在材质面板中出现的位置)。在Unity ShaderLab结构中,主要是Properties,SubShader,Fallback等。

     <1>Properties,顾名思义即“属性”,通俗点说就是提供在编辑器材质面板上让开发者输入接收属性调整各种材质属性的直接调试的机会。 因需要在shader程序中访问这些属性赋值,所以每个需要有一唯一的名字(Name),这个Name的用途仅是在shader程序中做区分。在Unity中,属性名字通常由一下划线开始。显示的名称(display name)则是出现在材质面板上的名字,显然要对其指定类型(PropertyType)。为了第一次把Unity shader赋给某材质时,材质面板上能显示初始值,所以需要为每个属性指定一个默认值。

    

     如在上述代码中添加属性Property,

Shader "shader/shader_5" {
Properties {
_testInt("fuckInt",Int) =
_testFloat("fuckFloat",Float) = 1.68
_testRange("fuckRange",Range(0.0, 10.0)) = 6.8 _testColor("fuckColor",Color) = (0.0, 1.0, 0.0, 0.0)
_testVector("fuckVector",Vector) = (,,,)
//定义2D贴图 2的阶数大小(256*318)之类的贴图,这张贴图在采样后被转为对应基于模型UV的每个像素的颜色最终显示
_test2D("2D贴图",2D) = ""{}
//定义立方体贴图 简单说就是6张有联系的贴图的组合,主要用来做反射效果(比如天空盒动态反射),也会被转换为对应点的采样
_testCube("Cube贴图",Cube) = "white"{}
//_test3D("fuck3D",3D) = "black"{}
} SubShader {
Pass {
CGPROGRAM fixed4 _testColor;//注意要使用Properties属性值时必须先声明 #pragma vertex vert
#pragma fragment frag float4 vert(float4 v : POSITION) : SV_POSITION {
return UnityObjectToClipPos(v);
} fixed4 frag() : SV_Target {
return _testColor;
} ENDCG
}
}
}

     效果如下图所示,颜色可直接在材质面板中调整:

    

     <2>SubShader和Pass语义块,用来说明如何渲染,每个shader文件可以包含多个SubShader语义块,当Unity加载这个shader文件时会按顺序从头扫描所有SubShader,然后选择第一个平台支持的SubShader文件,若都不支持则选择Fallback指定的shader。而每个Pass定义了一次完整的渲染流程,Pass数目较多会导致渲染性能的下降,所以尽量使用最少数量的Pass。  在SubShader和Pass中都可以设置渲染相关的状态(如是否开启混合和测试等)和标签,但各自的标签内容不同的故不能共用,后面会分开说明。当在SubShader中设置了渲染状态,则会应用到所有的Pass块,否则要单独在Pass中声明。

    

     SubShader的标签是字符串组成的键值对,用来告诉unity引擎希望怎样以及何时渲染这个对象,标签值和Pass的不一样。编写格式是:  Tags {"tagName" = "value"  "tagName2" = "value2"}

    

     Pass语义块多了个name,用来区分每一个pass块,并且通过这个名称,可以使用UsePass命令来直接使用其他Unity Shader中的Pass块。例如在Pass中添加 Name "MyPassName",那在别的模块就能通过 UsePass “MyShader/MYPASSNAME" 进行一样的渲染处理,有利于提高代码的复用性。 要注意一点,使用UsePass指令时Pass的名称必须所有字母都是大写模式!

    

    

     在HelloWorld工程中不需要任何渲染设置和标签设置,只需要编写CG代码块片段,由CGPROGRAM开始到ENDCG结束包围。 首先,是两条以"#pragma标志的编译指令,将告诉unity哪个函数包含了顶点着色器的代码,哪个函数包含了片段着色器代码。 #pragma vertex name 这里name就是我们指定的函数名,不一定是vert或frag,可以是任何自定义的合法函数名。

     重点分析vert函数的定义:

float4 vert(float4 v : POSITION) : SV_POSITION{
return mul(UNITY_MATRIX_MVP, v)
}

      顶点着色器代码,是逐顶点执行的。这里POSITION和SV_POSITION都是CG中的语义,是不可忽略的,这些语义告诉unity用户需要哪些输入值,以及用户输出是什么。如POSITION将告诉Unity,把模型的顶点坐标填充到输入参数v中;SV_POSITION将告诉Unity顶点着色器的输出是裁剪空间中的顶点坐标 。如果没有语义说明,渲染器就完全不知道用户的输入输出是什么,会得到错误的效果。  运行程序,会发现unity会自动替换mul()函数为UnityObjectToClipPos,原因在于:所有内建的矩阵名字在Instanced Shader中都是被重定义过的,如果直接使用UNITY_MATRIX_MVP,会引入一个额外的矩阵乘法运算。推荐使用UnityObjectToClipPos / UnityObjectToViewPos函数,会把这一次额外的矩阵乘法优化为向量-矩阵乘法。

      同理,frag函数SV_Target语义告诉渲染器,把用户输出颜色存储到一个渲染目标中,这里将默认输出到默认的帧缓存中。

     扩展模型输入数据

      上面的代码只能通过POSITION语义获得模型的顶点坐标,要是还想获得模型每个顶点的纹理坐标和法线方向,该怎么办呢?因为使用纹理坐标来访问对纹理采样,而用法线数据计算光照等都是很正常的需求,这就要求定义一新的输入参数(如结构体等),而不再是简单的一个数据类型。同理希望输出纹理坐标,法线等数据到fragment片元着色器,这也需要结构体。 修改的代码如下:

    SubShader {
Pass {
CGPROGRAM fixed4 _testColor;//注意要使用Properties属性值时必须先声明 #pragma vertex vert
#pragma fragment frag struct a2v
{
//POSITION语义:用模型空间的顶点坐标填充vertex变量
float4 vertex:POSITION;
//NORMAL语义:用模型空间的法线方向填充normal变量
float3 normal:NORMAL;
//TEXCOORD0语义:用模型的第一套纹理坐标填充texcoord变量
float4 texcoord:TEXCOORD0;
}; struct v2f
{
//SV_POSITION语义:把顶点在裁剪空间中位置信息填充pos变量
float4 pos:SV_POSITION;
//COLOR0语义:存储颜色信息
float3 color:COLOR0;
}; float4 vert(a2v v) : SV_POSITION {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.color = v.nonrmal * 0.5 + fixed3(0.5,0.5,0.5)
return o
} fixed4 frag(v2f i) : SV_Target {
return fixed4(i.color, 1.0);
} ENDCG
}
}

      其中,a2v表示把数据从应用阶段(application)传递到顶点着色器中。那填充到POSITION,NORMAL等语义中的数据是从哪来的呢?在Unity中,它们是由使用该材质的Mesh Render组件提供。在每帧调用draw Call的时候,Mesh Render组件会把它负责渲染的模型数据发送给Unity Shader。而一个模型通常包含一组三角面片,每个三角面片由3个顶点构成,每个顶点又包含一些数据,如顶点位置,法线,纹理坐标,顶点颜色等等。

小强学渲染之Unity Shader编程HelloWorld的更多相关文章

  1. 小强学渲染之Unity Shader噪声应用

    之前玩Tencent的仙剑4手游时,杀死boss会看到boss有“消融”的效果,就是身体上有多个洞洞然后往四周扩散直至尸体完全消失,但效果是没有关闭背面剔除的“穿帮”效果,可能也是考虑性能因素. em ...

  2. 小强学渲染之Unity Shader边缘描边加强

    项目开发遇到一个需求,就是当坦克的准心瞄准敌方(enemy tank 或 item box)时,要让选中的对象的轮廓高亮起来,这实际上是接下来要讲解的实时渲染中轮廓线的渲染应用.实现方式有多种,下面逐 ...

  3. 小强学渲染之OpenGL的CPU管线

    读到这里,应该对OpenGL渲染管线有了初步简单了解.下面着重分析CPU管线,即逻辑控制中心做了什么,这部分还是容易理解的.如下图: 一,将数据加载到显存中. 这是由GPU是访问显存中的数据决定的.因 ...

  4. 小强学渲染之OpenGL渲染管线详析

    什么是OpenGL? OpenGL是一套图形硬件的软件API接口库,它直接和GPU交互,将3D场景渲染绘制到2D屏幕上.总结说,OpenGL的功能是将程序中定义的各种2D或3D模型绘制到帧缓存中,或者 ...

  5. 小强学渲染之OpenGL的GPU管线

    GPU渲染流水线,是硬件真正体现渲染概念的操作过程,也是最终将图元画到2D屏幕上的阶段.GPU管线涵盖了渲染流程的 几何阶段 和 光栅化阶段,但对开发者而言,只有对顶点和片段着色器有可编程控制权,其他 ...

  6. 【Unity Shader编程】之十六 基于MatCap实现适于移动平台的“次时代”车漆Shader

    本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://blog.csdn.net/poem_qianmo/article/details/55803629 渲染本文配图使用的 ...

  7. 小强学渲染之OpenGL状态机理解

    状态机是理论上的一种机器,呃这个说法非常非常的抽象.通俗一点理解,状态机描述了一个对象在其生命周期内所经历的各种状态,状态间的转变,发生转变的动因,条件及转变中所执行的活动.或者说,状态机是一种行为, ...

  8. 【浅墨Unity3D Shader编程】之一 夏威夷篇:游戏场景的创建 & 第一个Shader的书写

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/40723789 作者:毛星云(浅墨)  ...

  9. 【浅墨Unity3D Shader编程】之中的一个 夏威夷篇:游戏场景的创建 &amp; 第一个Shader的书写

    本系列文章由@浅墨_毛星云 出品.转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/40723789 作者:毛星云(浅墨)  ...

随机推荐

  1. 怎么安装Scrapy框架以及安装时出现的一系列错误(win7 64位 python3 pycharm)

    因为要学习爬虫,就打算安装Scrapy框架,以下是我安装该模块的步骤,适合于刚入门的小白: 一.打开pycharm,依次点击File---->setting---->Project---- ...

  2. pyexecjs模块

    1,找到断点事件 2,浏览call stack 下面的代码,找到需要的值 3,F11进入方法内部 对于网站自己定义的方法,可以用py复制下来进行执行js N = function (a, b, c) ...

  3. LeetCode【110. 平衡二叉树】

    对于平衡二叉树,就是左右深度相差1 就可以另外弄一个函数,计算深度,然后, 在原函数上进行比较深度是否相差1,再输出true or false. 至于迭代就可以,比较完左右节点,再比较各自的左右节点. ...

  4. CentOS7的内核优化

    修改内核配置文件 vim /etc/sysctl.conf 刷新配置文件 sysctl -p 关ipv6 net.ipv6.conf.all.disable_ipv6 = net.ipv6.conf. ...

  5. python之数据类型详解

    python之数据类型详解 二.列表list  (可以存储多个值)(列表内数字不需要加引号) sort s1=[','!'] # s1.sort() # print(s1) -->['!', ' ...

  6. 统计每日单量MySQL语句

    -- 每日单量 select DATE_FORMAT(createtime,'%Y-%m-%d') as days,count(*) count from ibt_shop_order group b ...

  7. mybatis入门篇:Mybatis注解方式的基本用法

    @Select 1.mybatis-config.xml <?xml version="1.0" encoding="UTF-8" ?> <! ...

  8. 观察者模式的python实现

    什么会观察者模式?观察者模式就是订阅-推送模式.是为了解耦合才会被利用起来的设计模式. 经典的就是boss 前台和员工之间的故事.一天A员工在看电影,B员工在看动漫,但是两人担心boss来了,自己没及 ...

  9. Cache-control使用Cache-control:private学习笔记【转载】

    网页缓存由 HTTP消息头中的Cache-control控制,常见取值有private.no-cache.max-age.must- revalidate等,默认为private 其作用根据不同的重新 ...

  10. 列举spark所有算子

    一.RDD概述      1.什么是RDD           RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可 ...