初次学习shader

Shader "Custom/Diffuse Texture" {        //在shader中的位置
  Properties {                   //着色器的属性
      _MainTex ("Base (RGB)", 2D) = "white" {}//每一条属性的定义的语法是这样的:_Name("Display Name", type) = defaultValue[{options}] 注释:①
  }
  SubShader {                   //表面着色器
      Tags { "RenderType"="Opaque" }      //标签,修饰表面着色器 注释②
      LOD 200                    //不能小于Subshader的LOD值  注释③

      CGPROGRAM                  //shader 的本体使用的是cg语言或是HLSL语言,最后一行ENDCG和CGPROGRAM是对应的,表明CG程序到此结束
      #pragma surface surf Lambert  

                          /*接下来是是一个编译指令:#pragma surface surf Lambert,它声明了我们要写一个表面Shader,并指定了光照模型。它的写法是这样的
                            #pragma surface surfaceFunction lightModel [optionalparams]
surface - 声明的是一个表面着色器
surfaceFunction - 着色器代码的方法的名字
lightModel - 使用的光照模型。
                           */
      sampler2D _MainTex;                 //在CG中,sampler2D就是和texture所绑定的一个数据容器接口,需要再次声明才可以使用Properties定义的变量 注释④

      struct Input {
          float2 uv_MainTex;
      };

      void surf (Input IN, inout SurfaceOutput o) {  //#pragma段已经指出了我们的着色器代码的方法的名字叫做surf //注释⑤
          half4 c = tex2D (_MainTex, IN.uv_MainTex);
          o.Albedo = c.rgb;
          o.Alpha = c.a;
      }
      ENDCG
  }
  FallBack "Diffuse"
}

一个完整的shader

注释:

①_Name("Display Name", type) = defaultValue[{options}]

  • _Name - 属性的名字,简单说就是变量名,在之后整个Shader代码中将使用这个名字来获取该属性的内容
  • Display Name - 这个字符串将显示在Unity的材质编辑器中作为Shader的使用者可读的内容
  • type - 这个属性的类型,可能的type所表示的内容有以下几种:
    • Color - 一种颜色,由RGBA(红绿蓝和透明度)四个量来定义;
    • 2D - 一张2的阶数大小(256,512之类)的贴图。这张贴图将在采样后被转为对应基于模型UV的每个像素的颜色,最终被显示出来;
    • Rect - 一个非2阶数大小的贴图;
    • Cube - 即Cube map texture(立方体纹理),简单说就是6张有联系的2D贴图的组合,主要用来做反射效果(比如天空盒和动态反射),也会被转换为对应点的采样;
    • Range(min, max) - 一个介于最小值和最大值之间的浮点数,一般用来当作调整Shader某些特性的参数(比如透明度渲染的截止值可以是从0至1的值等);
    • Float - 任意一个浮点数;
    • Vector - 一个四维数;
  • defaultValue 定义了这个属性的默认值,通过输入一个符合格式的默认值来指定对应属性的初始值(某些效果可能需要某些特定的参数值来达到需要的效果,虽然这些值可以在之后在进行调整,但是如果默认就指定为想要的值的话就省去了一个个调整的时间,方便很多)。
    • Color - 以0~1定义的rgba颜色,比如(1,1,1,1);
    • 2D/Rect/Cube - 对于贴图来说,默认值可以为一个代表默认tint颜色的字符串,可以是空字符串或者”white”,”black”,”gray”,”bump”中的一个
    • Float,Range - 某个指定的浮点数
    • Vector - 一个4维数,写为 (x,y,z,w)
    • 另外还有一个{option},它只对2D,Rect或者Cube贴图有关,在写输入时我们最少要在贴图之后写一对什么都不含的空白的{},当我们需要打开特定选项时可以把其写在这对花括号内。如果需要同时打开多个选项,可以使用空白分隔。可能的选择有ObjectLinear, EyeLinear, SphereMap, CubeReflect, CubeNormal中的一个

栗子 :

//定义一个颜色

_MainColor ("Main Color", Color) = (0,0,1,0.5)

//定义一个2D的图片

_Texture ("Texture", 2D) = "white" {}

②Tags { "RenderType"="Opaque" }

告诉了系统应该在渲染非透明物体时调用我们。Unity定义了一些列这样的渲染过程,与RenderType是Opaque相对应的显而易见的是"RenderType" = "Transparent",表示渲染含有透明效果的物体时调用。在这里Tags其实暗示了你的Shader输出的是什么,如果输出中都是非透明物体,那写在Opaque里;如果想渲染透明或者半透明的像素,那应该写在Transparent中。

另外比较有用的标签还有"IgnoreProjector"="True"(不被Projectors影响),"ForceNoShadowCasting"="True"(从不产生阴影)以及"Queue"="xxx"(指定渲染顺序队列)。这里想要着重说一下的是Queue这个标签,如果你使用Unity做过一些透明和不透明物体的混合的话,很可能已经遇到过不透明物体无法呈现在透明物体之后的情况。这种情况很可能是由于Shader的渲染顺序不正确导致的。Queue指定了物体的渲染顺序,预定义的Queue有:

  • Background - 最早被调用的渲染,用来渲染天空盒或者背景
  • Geometry - 这是默认值,用来渲染非透明物体(普通情况下,场景中的绝大多数物体应该是非透明的)
  • AlphaTest - 用来渲染经过Alpha Test的像素,单独为AlphaTest设定一个Queue是出于对效率的考虑
  • Transparent - 以从后往前的顺序渲染透明物体
  • Overlay - 用来渲染叠加的效果,是渲染的最后阶段(比如镜头光晕等特效)

这些预定义的值本质上是一组定义整数,Background = 1000, Geometry = 2000, AlphaTest = 2450, Transparent = 3000,最后Overlay = 4000。在我们实际设置Queue值时,不仅能使用上面的几个预定义值,我们也可以指定自己的Queue值,写成类似这样:"Queue"="Transparent+100",表示一个在Transparent之后100的Queue上进行调用。通过调整Queue值,我们可以确保某些物体一定在另一些物体之前或者之后渲染,这个技巧有时候很有用处。

③LOD 200

LOD很简单,它是Level of Detail的缩写,在这里例子里我们指定了其为200(其实这是Unity的内建Diffuse着色器的设定值)。这个数值决定了我们能用什么样的Shader。在Unity的Quality Settings中我们可以设定允许的最大LOD,当设定的LOD小于SubShader所指定的LOD时,这个SubShader将不可用。Unity内建Shader定义了一组LOD的数值,我们在实现自己的Shader的时候可以将其作为参考来设定自己的LOD数值,这样在之后调整根据设备图形性能来调整画质时可以进行比较精确的控制。

  • VertexLit及其系列 = 100
  • Decal, Reflective VertexLit = 150
  • Diffuse = 200
  • Diffuse Detail, Reflective Bumped Unlit, Reflective Bumped VertexLit = 250
  • Bumped, Specular = 300
  • Bumped Specular = 400
  • Parallax = 500
  • Parallax Specular = 600

④sampler2D _MainTex

在CG中,sampler2D就是和texture所绑定的一个数据容器接口。简单理解的话,所谓加载以后的texture(贴图)说白了不过是一块内存存储的,使用了RGB(也许还有A)通道,且每个通道8bits的数据。而具体地想知道像素与坐标的对应关系,以及获取这些数据,我们总不能一次一次去自己计算内存地址或者偏移,因此可以通过sampler2D来对贴图进行操作。更简单地理解,sampler2D就是GLSL中的2D贴图的类型,相应的,还有sampler1D,sampler3D,samplerCube等等格式。

解释通了sampler2D是什么之后,还需要解释下为什么在这里需要一句对_MainTex的声明,之前我们不是已经在Properties里声明过它是贴图了么。

答案是我们用来实例的这个shader其实是由两个相对独立的块组成的,外层的属性声明,回滚等等是Unity可以直接使用和编译的ShaderLab;而现在我们是在CGPROGRAM...ENDCG这样一个代码块中,这是一段CG程序。对于这段CG程序,要想访问在Properties中所定义的变量的话,必须使用和之前变量相同的名字进行声明。于是其实sampler2D _MainTex;做的事情就是再次声明并链接了_MainTex,使得接下来的CG程序能够使用这个变量。

注释⑤:

着色器就是给定了输入,然后给出输出进行着色的代码。CG规定了声明为表面着色器的方法(就是我们这里的surf)的参数类型和名字,因此我们没有权利决定surf的输入输出参数的类型,只能按照规定写。这个规定就是第一个参数是一个Input结构,第二个参数是一个inout的SurfaceOutput结构。

它们分别是什么呢?Input其实是需要我们去定义的结构,这给我们提供了一个机会,可以把所需要参与计算的数据都放到这个Input结构中,传入surf函数使用;

SurfaceOutput是已经定义好了里面类型输出结构,但是一开始的时候内容暂时是空白的,我们需要向里面填写输出,这样就可以完成着色了。先仔细看看INPUT吧。

<font style="color:rgb(102, 102, 102)">struct Input {
  float2 uv_MainTex;
};</font>

  作为输入的结构体必须命名为Input,这个结构体中定义了一个float2的变量…你没看错我也没打错,就是float2,表示浮点数的float后面紧跟一个数字2,这又是什么意思呢?其实没什么魔法,float和vec都可以在之后加入一个2到4的数字,来表示被打包在一起的2到4个同类型数。比如下面的这些定义

<font style="color:rgb(102, 102, 102)">//Define a 2d vector variable
vec2 coordinate;
//Define a color variable
float4 color;
//Multiply out a color
float3 multipliedColor = color.rgb * coordinate.x;</font>

  在访问这些值时,我们即可以只使用名称来获得整组值,也可以使用下标的方式(比如.xyzw,.rgba或它们的部分比如.x等等)来获得某个值。

在这个例子里,我们声明了一个叫做uv_MainTex的包含两个浮点数的变量。如果你对3D开发稍有耳闻的话,一定不会对uv这两个字母感到陌生。UV mapping的作用是将一个2D贴图上的点按照一定规则映射到3D模型上,是3D渲染中最常见的一种顶点处理手段。在CG程序中,我们有这样的约定,在一个贴图变量(在我们例子中是_MainTex)之前加上uv两个字母,就代表提取它的uv值(其实就是两个代表贴图上点的二维坐标 )。我们之后就可以在surf程序中直接通过访问uv_MainTex来取得这张贴图当前需要计算的点的坐标值了。

如果你坚持看到这里了,那要恭喜你,因为离最后成功读完一个Shader只有一步之遥。我们回到surf函数,它的两有参数,第一个是Input,我们已经明白了:在计算输出时Shader会多次调用surf函数,每次给入一个贴图上的点坐标,来计算输出。第二个参数是一个可写的SurfaceOutput,SurfaceOutput是预定义的输出结构,我们的surf函数的目标就是根据输入把这个输出结构填上。SurfaceOutput结构体的定义如下

<font style="color:rgb(102, 102, 102)">struct SurfaceOutput {
    half3 Albedo;     //像素的颜色
    half3 Normal;     //像素的法向值
    half3 Emission;   //像素的发散颜色
    half Specular;    //像素的镜面高光
    half Gloss;       //像素的发光强度
    half Alpha;       //像素的透明度
};</font>

这里的half和我们常见float与double类似,都表示浮点数,只不过精度不一样。也许你很熟悉单精度浮点数(float或者single)和双精度浮点数(double),这里的half指的是半精度浮点数,精度最低,运算性能相对比高精度浮点数高一些,因此被大量使用。在例子中,我们做的事情非常简单

half4 c = tex2D (_MainTex, IN.uv_MainTex);

o.Albedo = c.rgb;

o.Alpha = c.a;

这里用到了一个tex2d函数,这是CG程序中用来在一张贴图中对一个点进行采样的方法,返回一个float4。这里对_MainTex在输入点上进行了采样,并将其颜色的rbg值赋予了输出的像素颜色,将a值赋予透明度。于是,着色器就明白了应当怎样工作:即找到贴图上对应的uv点,直接使用颜色信息来进行着色,over。接下来…我想现在你已经能读懂一些最简单的Shader了,接下来我推荐的是参考Unity的Surface Shader Examples多接触一些各种各样的基本Shader。在这篇教程的基础上,配合一些google的工作,完全看懂这个shader示例页面应该不成问题。如果能做到无压力看懂,那说明你已经有良好的基础可以前进到Shader的更深的层次了(也许等不到我的下一篇教程就可以自己开始动手写些效果了);如果暂时还是有困难,那也没有关系,Shader学习绝对是一个渐进的过程,因为有很多约定和常用技巧,多积累和实践自然会进步并掌握。在接下来的教程里,打算通过介绍一些实际例子以及从基础开始实际逐步动手实现一个复杂一点的例子,让我们能看到shader在真正使用中的威力

学习来源于:http://www.manew.com/thread-1432-1-1.html

时间: 2024-08-06 22:19:14

初次学习shader的相关文章

学习shader之前必须知道的东西之计算机图形学(一)渲染管线

引言 shader到底是干什么用的?shader的工作原理是什么? 其实当我们对这个问题还很懵懂的时候,就已经开始急不可耐的要四处搜寻有关shader的资料,恨不得立刻上手写一个出来.但看了一些资料甚至看了不少cg的语法之后,我们还是很迷茫,UNITY_MATRIX_MVP到底是个什么矩阵?它和v.vertex相乘出来的又是什么玩意?当这些问题困扰我们很久之后,我们才发现,原来我们是站在浮沙上筑高台,根基都没有打牢当然不可能盖得起高楼大厦了. 那根基是什么呢?大牛曰,计算机图形学. shader

关于egg.js的初次学习——controller和router的基本使用

今天学习了egg最基本的controller和router的使用. 首先什么是controller和router呢?controller就是经典的MVC(module,view,controller)架构中的controller层,主要用来解决实际的业务逻辑. router主要是用来分发来自页面的请求,然后把这个请求交给某个controller去做.此处来一个小栗子 1 module.exports = app => { 2 app.get('/', 'render.ejs'); 3 }; 这里

jQuery的初次学习

今天学习到了jQuery的应用,第一次使用jQuery,我就感受到了它的强大,我之所以强大的原因是它的选择器,它能满足几乎所有我所能想到的找到一个节点的方法,它包括了所有CSS的选择器,而且完善了选择器.今天就来说说关于jQ的选择器吧. 首先要说的是过滤性选择器,第一个:even,匹配所有索引值为偶数的元素,从 0 开始计数.具体的例子就不说了,手册上都有.然后是与之相对应的:odd,也就是匹配奇数的.这两个选择器,在制作类似表格的时候会比较好用.下一个:eq(index),这也是一个常用的选择

初次学习AngularJS

一.指令1.AngularJS 指令是扩展的 HTML 属性,带有前缀 ng-. ng-app 指令初始化一个 AngularJS 应用程序. ng-app 指令定义了 AngularJS 应用程序的 根元素. ng-app 指令在网页加载完毕时会自动引导(自动初始化)应用程序. 稍后您将学习到 ng-app 如何通过一个值(比如 ng-app="myModule")连接到代码模块. 2.ng-init 指令初始化应用程序数据. ng-init 指令为 AngularJS 应用程序定义

Unity Shader入门精要学习笔记 - 第4章 学习 Shader 所需的数学基础

摘录自 冯乐乐的<Unity Shader入门精要> 笛卡尔坐标系 1)二维笛卡尔坐标系 在游戏制作中,我们使用的数学绝大部分都是计算位置.距离.角度等变量.而这些计算大部分都是在笛卡尔坐标系下进行的. 一个二维的笛卡尔坐标系包含了两个部分的信息: 一个特殊的位置,即原点,它是整个坐标系的中心. 两条过原点的互相垂直的矢量,即X轴和Y轴.这些坐标轴也被称为是该坐标的矢量. OpenGL 和 DirectX 使用了不同的二维笛卡尔坐标系.如下图所示: 2)三维笛卡尔坐标系 在三维笛卡尔坐标系中,

【系统学习Shader与后处理】第2篇:uv控制之water

我们每要实现一个效果,就如这个系列开头所说,先分析效果需要用什么模式来实现. 我们发现,水流动的效果跟uv有关,所以我们需要去控制uv. 控制uv一般有三种方式: 1.time 2.采样其他图 3.C#输入参数 然后根据这些参数,用某个公式实现 这个shader,使用了time + 采样其他图 +C#输入参数 使用time,是为了达成两个目的, 第一个目的是time参数平滑递增, 第二个目的是uv本身的特点,是随着超出uv的范围后,又会回到uv起始位置. 所以使用了time,我们就可以让水流动起

第三章 学习Shader所需的数学基础(5)

1. Unity Shader的内置变量(数学篇) 使用Unity写shader的一个好处在于,它提供了很多内置参数,这使得我们不在需要自己手动算一些值.本文给出Unity内置的用于空间变换和摄像机以及屏幕参数的内置变量.这些内置变量可以在UnityShaderVariables.cginc文件中找到定义和说明. 1.1 变换矩阵 首先是用于坐标空间变换的矩阵.表中给出了Unity5.2版本提供的所有内置变换矩阵.下面的所有矩阵都是float4×4类型的. 上表给出了这些矩阵的常用用法.但读者可

Nginx初次学习三个知识点记录

1.Nginx是用来解决负载均衡问题的,它可以将请求均衡的发放到集群的每一个节点,但是使用者又拥有极大的控制权,通俗来讲就是你既可以让请求均衡发放,也可以指定哪些服务器接收更多的请求.更多的时候是用在集群服务器性能高低不一,希望性能高的服务器接收更多的请求. 2.当我们真正使用到Nginx时,就一定会涉及到集群分布式.而老生常谈的一个问题就是分布式情况下如何解决Session共享问题?我们不可能将其存在集群中的一台服务器上,其他服务器就会出现Session丢失的问题.同时还要保证Session的

初次学习Linux需要注意的

1.Linux严格区分大小写 2.Linux 中所有的内容以文件形式保存,包括硬件 硬盘文件时/dev/sd[a-p] 光盘文件时/dev/sr0等 3.Linux不靠扩展名区分文件类型,但是我们常常加一些扩展名来区分文件. 如:压缩包:.gz  .bz2    .tar.bz2   .tgz等 二进制软件包:.rpm 网页文件:.html    .php 等 脚本文件: .sh 配置文件: .conf 4.Linux 所有的存储设备都必须挂载之后用户才可以使用,包括硬盘.U盘和光盘 5.Win