第一个简单的顶点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结构中,主要是PropertiesSubShaderFallback等。

     <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. spring 事物不回滚

    使用spring控制事物,为什么有些情况事物,事物不回滚呢?? 默认spring事务只在发生未被捕获的 RuntimeException时才回滚.   spring aop  异常捕获原理: 被拦截的 ...

  2. 去中心化存储项目终极指南 | Filecoin, Storj 和 PPIO 项目异同

    Filecoin,Storj 以及 PPIO 这三个存储公链的设计思路是不一样的,没有优劣之分,写这篇文章也并不是为了争论各项目的好坏对错.去中心化存储是一个长期商业赛道,不同团队在同一个赛道上往不同 ...

  3. 喜怨交加C++

    作为被高中理综折磨几年立誓大学不学理工科类的文字爱好者,会学计算机是个意外.出于对理工科的不感兴趣,暑假期间也没有预习过编程知识.在对编程进行学习之前,我将它视为一门科目.一个专业.一项技能,唯独不是 ...

  4. linux centos7下mysql安装--韩国庆

    首先我先给大家介绍下MariaDB和mysql的区别. 上图,“MySQL之父”的骨灰级程序员Monty,但是mysql被Oracle收购后,Monty又开始去发展另一条数据库的道路,并且以Monty ...

  5. VM12中CentOS7以NAT方式连接网络的方法

    解决问题:centos网络连不上,连不上主机,ifconfig等命令不能用(配完有网了,安装上就好了)等问题 前提:安装vm12,centos7(最小安装)  注意:以下以192开头的,你都要替换成自 ...

  6. 三、CSS样式——列表

    概念: CSS列表属性允许你放置.改变列表标志,或者将图像作为列表项标志 属性 描述 list-style 简写列表项 list-style-image 列表项图像 list-style-positi ...

  7. JPA相关知识

    这篇文章是摘自Patrick Linskey的一篇文章,主要是关于JPA相关内容的问答,相信JPA面试会碰到很多这里面的问题 问题:EJB专家团队是如何摆脱事务描述符的? 回答:在会话bean和消息驱 ...

  8. 走进HashMap

    1.手写实现HashMap 2.解析代码并阐述HashMap1.7到1.8的变化 3.HashMap的遍历方式 4.HashMap与HashSet,Hashtable,ConcurrentHashMa ...

  9. (python基础 函数)

    关键字参数:关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值.使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值 必需参数:必 ...

  10. 域名动态解析到动态IP

    一般宽带用户的IP都是动态IP,重连之后IP可能会发生变化. 如果想在其他地方连接家里的设备,或者在家中搭建服务器,就会受到影响. 现在提供一种动态解析域名的方式,只要检测到IP的变化,那么就调用阿里 ...