UnityShader快速上手指南(二)

时间:2021-11-17 00:23:21

简介

前一篇介绍了如果编写最基本的shader,接下来本文将会简单的深入一下,我们先来看下效果吧

UnityShader快速上手指南(二)

呃,gif效果不好,实际效果是很平滑的动态过渡

实现思路

1.首先我们要实现一个彩色方块

2.让色彩动起来

over

实现一个RGB CUBE

先看代码吧:

Shader "LT/Lesson2"
{
Properties {
_OffsetX ("Offset X", Range (-1.5, 1.5) ) = 0
_OffsetY ("Offset Y", Range (-1.5, 1.5) ) = 0
_OffsetZ ("Offset Z", Range (-1.5, 1.5) ) = 0
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc" struct VertextOutput {
float4 pos : SV_POSITION ;
fixed4 col : COLOR ;
}; uniform float _OffsetX;
uniform float _OffsetY;
uniform float _OffsetZ; VertextOutput vert ( appdata_base input )
{
VertextOutput result;
result.pos = mul(UNITY_MATRIX_MVP , input.vertex ) ;
result.col = input.vertex + float4( _OffsetX, _OffsetY, _OffsetZ, 0);
return result;
} fixed4 frag ( VertextOutput input ) : COLOR
{
return input.col;
} ENDCG
}
}
}

恩~~,首先呢,我们这次输出的颜色不同的位置颜色不同,所以我们需要一个同时能存位置和颜色的结构体:

			struct VertextOutput {
float4 pos : SV_POSITION ;
// 位置信息, 后面的: SV_POSITION是必须的,当然你也可以换成: POSITION
fixed4 col : COLOR ;
// 颜色信息, 后面的: COLOR不是必须的,你可以随便取名字比如 : FUCK
// 但是嘛,为了代码方便阅读,还是写成COLOR吧
};

然后呢我们只有这样一个模型:

UnityShader快速上手指南(二)

24个顶点(每个面顶点单算的),12个三角形,两个空的UV(这个是unity自带的cube模型)

这个模型是没有任何颜色信息,所以我们需要自己在shader中生成他的颜色

出于方便考虑,我们将这个模型的顶点(XYZ)变成RGB的颜色,因为刚好三个值都有变化嘛

于是有了这样的代码

result.col = input.vertex + float4( _OffsetX, _OffsetY, _OffsetZ, 0);

前面顶点位置就不作处理了,直接换算成Unity坐标就完了

result.pos = mul(UNITY_MATRIX_MVP , input.vertex ) ;

然后我们来说说传入参数中的appdata_base

对于VertextOutput vert ( appdata_base input )这个函数命名

学过C语言的应该知道前面是返回值类型 括号里面是传入值类型和名字吧

然后这个appdata_base呢是定义在#include "UnityCG.cginc"的一个结构体

(强行带节奏引入了UnityCG.cginc,其实也可以像前面一篇一样使用float4 position : POSITION,只是这里为了早点引入UnityCG.cginc而已)

我们可以看一下UnityCG.cginc的部分代码:

// Dynamic & Static lightmaps contain indirect diffuse ligthing, thus ignore SH
#define UNITY_SHOULD_SAMPLE_SH ( defined (LIGHTMAP_OFF) && defined(DYNAMICLIGHTMAP_OFF) ) struct appdata_base {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
}; struct appdata_tan {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
}; struct appdata_full {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 texcoord1 : TEXCOORD1;
float4 texcoord2 : TEXCOORD2;
float4 texcoord3 : TEXCOORD3;
#if defined(SHADER_API_XBOX360)
half4 texcoord4 : TEXCOORD4;
half4 texcoord5 : TEXCOORD5;
#endif
fixed4 color : COLOR;
};

整个有点小长,我只粘贴一部分,其实就是一大堆Unity的定义而已,

我们再来看看

struct appdata_base {
float4 vertex : POSITION; //位置
float3 normal : NORMAL; //法线
float4 texcoord : TEXCOORD0; // 纹理
};

其实这是一个简化的模型数据,包含了一些常用的参数,如果我们写的shader主要是给手机使用的话,这些数据基本也就够了,而且目前我们也就用了他的位置的信息,当然你也可以传入一个appdata_full 类型,区别不大

至于_OffsetX,_OffsetY,_OffsetZ三个外接属性的定义就不多做赘述了

然后我们就通过

			VertextOutput vert (  appdata_base input )
{
VertextOutput result;
result.pos = mul(UNITY_MATRIX_MVP , input.vertex ) ;
result.col = input.vertex + float4( _OffsetX, _OffsetY, _OffsetZ, 0);
return result;
}

计算出了相应顶点的颜色

然后直接在面片渲染函数中把对应点的颜色赋值给他就行了

return input.col;

注意这里我们的传入参数变成了vert 的返回值

fixed4 frag ( VertextOutput input ) : COLOR

好来看下初步的效果:

UnityShader快速上手指南(二)

这里我们就完成了一个RBG CUBE了。

下面对一些原理性的东西简单解释一下

光栅化?插值?

前面我解释过vert函数是一个顶点调用一次,这里我们的模型一共才24个顶点,但是为啥出来这么多个颜色呢,这里就跟渲染流程的光栅化有关。默认情况下,光栅化会保持平滑过渡,如果两边不匹配就会在中间插值,然后对于我们的模型而言,一个面上每个顶点的颜色都不同,所以他就会自动插入很多个顶点,并且自动渐变颜色来满足平滑过渡(也就是说如果你颜色都一样,就不会插点了,当然你也可以手动不让它插点,概念比较多,这里就不展开了,我们要快速上手嘛)

让颜色随着时间变化而变化

这里是我强行要加的一个功能,不然感觉这篇blog就太少内容咯,哈哈

有两种实现方法:

1.unity中通过C#代码去控制刚才开放出来的参数

2.shader中自己通过时间去更改颜色

我们既然是学shader,当然是在shader中进行更改啦

直接上代码:

result.col = input.vertex + float4( _SinTime.w + 0.5, _SinTime.w + 0.5, _SinTime.w + 0.5, 0);

呃,对,就改这一行。效果就是实现啦,大家可以自己行试一下

下面解释一小下下:

_SinTime是unity为shader内置的一个时间的sin值得变量(看名字也看的出来吧)

需要引入#include "UnityCG.cginc" (这也是为啥我前面强行带节奏的原因)

然后来普及下Unity为我们内置了哪些东西吧:

Transformations 变换

float4x4 UNITY_MATRIX_MVP
Current model*view*projection matrix
当前物体*视*投影矩阵。(注:物体矩阵为 本地->世界)
float4x4 UNITY_MATRIX_MV
Current model*view matrix
当前物体*视矩阵
float4x4 UNITY_MATRIX_P
Current projection matrix
当前物体*投影矩阵
float4x4 UNITY_MATRIX_T_MV
Transpose of model*view matrix
转置物体*视矩阵
float4x4 UNITY_MATRIX_IT_MV
Inverse transpose of model*view matrix
逆转置物体*视矩阵
float4x4 UNITY_MATRIX_TEXTURE0 to UNITY_MATRIX_TEXTURE3
Texture transformation matrices
贴图变换矩阵
float4x4 _Object2World
Current model matrix
当前物体矩阵
float4x4 _World2Object
Inverse of current world matrix
物体矩阵的逆矩阵
float3 _WorldSpaceCameraPos
World space position of the camera
世界坐标空间中的摄像机位置
float4 unity_Scale
xyz components unused; .w contains scale for uniformly scaled objects.
不适用xyz分量,而是通过w分量包含的缩放值等比缩放物体。 _ModelLightColor float4 Material's Main * Light color 材质的主颜色*灯光颜色
_SpecularLightColor float4 Material's Specular * Light color 材质的镜面反射(高光)*灯光颜色。
_ObjectSpaceLightPos float4 Light's position in object space. w component is 0 for directional lights, 1 for other lights
物体空间中的灯光为,平行光w分量为零其灯光为1;
_Light2World float4x4 Light to World space matrix 灯光转世界空间矩阵
_World2Light float4x4 World to Light space matrix 世界转灯光空间矩阵
_Object2Light float4x4 Object to Light space matrix 物体转灯光空间矩阵 float4 _Time : Time (t/20, t, t*2, t*3), use to animate things inside the shaders
时间: 用于Shasder中可动画的地方。
float4 _SinTime : Sine of time: (t/8, t/4, t/2, t)
时间的正弦值。
float4 _CosTime : Cosine of time: (t/8, t/4, t/2, t)
时间的余弦值
float4 _ProjectionParams : 投影参数
x is 1.0 or -1.0, negative if currently rendering with a flipped projection matrix
x为1.0 或者-1.0如果当前渲染使用的是一个反转的投影矩阵那么为负。
y is camera's near plane y是摄像机的近剪裁平面
z is camera's far plane z是摄像机远剪裁平面
w is 1/FarPlane. w是1/远剪裁平面
float4 _ScreenParams : 屏幕参数
x is current render target width in pixels x是当前渲染目标在像素值中宽度
y is current render target height in pixels y是当前渲染目标在像素值中的高度
z is 1.0 + 1.0/width z是1.0+1.0/宽度
w is 1.0 + 1.0/height w是1.0+1.0/高度

呃,格式不是很好看的样子,这里有链接,自己去看吧http://www.ceeger.com/Components/SL-BuiltinValues.html

有了这些东西之后呢,我们就可以简单的根据时间变化做一些动态shader了,比如什么UV流动啊,颜色动态变化啊,动态模型(是动态模型不是模型动画哈)啥的,瞬间就高大上了有不有,性能嘛取决于你写的代码(同样的代码级别下,shader速度秒杀你在c#中写)

总结

这一篇感觉写的比较乱,主要是知识点比较杂(这理由不是很好找啊....原谅我语文老师是数学老师教大的)

主要知识点是介绍一下光栅化那个插值,这个很重要https://en.wikibooks.org/wiki/Cg_Programming/Rasterization(虽然我讲的一笔带过,大家去看看官方解释吧)

然后介绍一下UnityCG.cginc,我们既然是写的unityshader,当然还是要经常使用这个库的啦,后面还有光照,空间矩阵啥的,基本我们要想做高级特效离不开这个库的,大家可以去看看这个库源码

了解了这些之后,就是单纯的算法了(比如怎么通过时间更改模型顶点位置实现好看的动画啥的)

恩~~~再次坦白下写的比较乱,如有疑问欢迎联系QQ:821580467一起探讨

UnityShader快速上手指南(二)的更多相关文章

  1. UnityShader快速上手指南(三)

    简介 这一篇还是一些基本的shader操作:裁剪.透明和法向量的应用 (纠结了很久写不写这些,因为代码很简单,主要是些概念上的东西) 先来看下大概的效果图:(从左到右依次是裁剪,透明,加了法向量的透明 ...

  2. UnityShader快速上手指南(一)

    简介 引言 其实网上有很多shader教程,但是大概看了下,也不知是网上各位大神已经脱离了代码层面的高度还是啥原因.貌似没有找到从代码方面作为入门讲解的,导致了shader对于苦逼程序员入门有一定要求 ...

  3. UnityShader快速上手指南(四)

    简介 由于其他项目中断了几天更新,继续~~ 这一篇主要是讲光照的(包含漫反射和高光以及多光源的处理) 还是先来看看具体效果(多光源后面单独展示) 有了基本的光照处理之后越来越有立体感了有不有 ╮(╯▽ ...

  4. Rancher 快速上手指南操作(1)

    Rancher 快速上手指南操作(1)该指南知道用户如何快速的部署Rancher Server 管理容器.前提是假设你的机器已经安装好docker了.1 确认 docker 的版本,下面是 ubunt ...

  5. [转]Rancher 快速上手指南操作(1)

    本文转自:http://www.cppblog.com/zhiyewang/archive/2016/03/17/213053.aspx Rancher 快速上手指南操作(1)该指南知道用户如何快速的 ...

  6. Markdown快速上手指南

    Markdown快速上手指南 1.Markdown介绍 markdown可以实现快速html文档编辑,格式优没,并且不需要使用html元素. markdown采用普通文本的形式,例如读书笔记等易于使用 ...

  7. Netron开发快速上手(二):Netron序列化

    Netron是一个C#开源图形库,可以帮助开发人员开发出类似Visio的作图软件.本文继前文”Netron开发快速上手(一)“讨论如何利用Netron里的序列化功能快速保存自己开发的图形对象. 一个用 ...

  8. Github Action 快速上手指南

    前言 各位读者,新年快乐,我是过了年匆忙赶回上海努力搬砖的蛮三刀. Github之前更新了一个Action功能(应该是很久以前了),可以实现很多自动化操作.用来替代用户自己设置的自动化脚本(比如:钩子 ...

  9. socket网络编程快速上手(二)——细节问题(5)(完结篇)

    6.Connect的使用方式 前面提到,connect发生EINTR错误时,是不能重新启动的.那怎么办呢,是关闭套接字还是直接退出进程呢?如果EINTR前,三次握手已经发起,我们当然希望链路就此已经建 ...

随机推荐

  1. Android 常用开发工具以及Mac常用软件

    Android 常用的开发工具记录.其中包括AndroidStudio(IDEA)插件.Mac 上好用的软件以及国内知名Android开发者博客等. Android Studio 插件 codota ...

  2. 神奇的CSS sprites,制作特效的新方法

    本文主要内容简译自Dava Shea的英文文章 CSS Sprites: Image Slicing’s Kiss of Death,如果觉得博主讲的含糊不清的话,可以看作者原文. 熟悉了常规切图的我 ...

  3. swift基础:第四部分:对函数和闭包的深入

    ()之前在什么公司,都自己做过哪些项目,从架构的角度来谈谈你的项目. () 你对iOS不同版本是怎么看的,你在做项目的过程当中,是如何应对版本问题的. () 你对iOS的性能是怎么优化的. () 你通 ...

  4. C++中的new与delete(二)

    C++一个对象构造的完整过程为:分配内存和初始化,这也是new关键字所实现的功能,分配内存可通过重载new操作符来实现,系统初始化可通过调用构造函数来完成.我们不能改变new关键字的功能,但可以改变分 ...

  5. [python]逆水行舟不进则退(1)

    工作后迎来的第一个长假期,打算在家休息一下,看看书之类的.但是不写点东西,不做点东西,感觉有些浪费时间.同时也想通过做点东西检验下自己这段时间的收获.其实在我开始写这篇文章的时候心里还是很没底的-交代 ...

  6. back_insert_iterator和iterator用起来不一样。

    先看代码: #include<iostream> #include<vector> #include<algorithm> #include<iterator ...

  7. sql中charindex和cast结合使用

    1.CHARINDEX函数常常用来在一段字符中搜索字符或者字符串. 语法 CHARINDEX ( expression1 , expression2 [ , start_location ] ) 返回 ...

  8. 10分钟学会Linux

    10分钟学会Linux有点夸张,可是能够让一个新手初步熟悉Linux中最重要最主要的知识,本文翻译的英文网页在众多Linux入门学习的资料中还是很不错的. 英文地址:http://freeengine ...

  9. 在Windows环境下设置terminal下调试adb

    当我们想要查看某些程序运行的结果的时候.可能需要打开adb,输入相应的命令,在Windows环境下,需要配置环境变量. 当我们直接在Windows环境下输入adb shell,会提示adb是不内部命令 ...

  10. 浅谈java中的&quot&semi;&equals;&equals;&quot&semi;和eqals区别

    在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str2 = new String(&qu ...