Rust 基础 | 所有权

什么是所有权 Rust 的所有权,是 Rust 语言的一个核心概念。可以简单理解为,一种内存管理的方式。用现实中的东西举例,当你从图书管借了一本书时,这本书的所有权暂时归你所有,而当你把书给你的朋友时,此刻,书的所有权归你的朋友所有,而当你的朋友把书归还图书馆时,此时没有人拥有书的所有权,相当于内存释放。 在使用有自动垃圾回收(GC)的编程语言时,我们并不需要考虑内存的释放问题,因为GC会帮我们释放。Rust是无GC的语言,一个变量占用的内存什么时候释放,由它的所有权决定,简单来说,当所有权所在的作用域结束时,内存将被释放。 什么是作用域 Rust 的作用域和其他编程语言中的作用域概念是一样的,我们使用下面的代码说明 // 整个 main 函数是一个作用域 fn main() { let a = 10; // 下面的花括号内,也是一个作用域 { let s = "hello"; println!("{}", s); } // 下面这句再次打印s,会编译出错,因为s所在的作用域已经结束,s 被释放掉了 //println!("s again: {}", s); } 关于堆内存和栈内存 我们知道,内存分为堆和栈。存在栈上的数据,必须是已知固定大小的数据。而存在堆上的数据,都是在编译时不知道大小的数据,例如用户自己输入的数据。栈比堆的访问快很多,这是因为栈的存取结构,都是操作栈顶,不需要去内存中找数据。而要将数据存在堆上,则需要向操作系统申请,由操作系统在内容中找到一块能够容纳你要存的数据大小的内存空间,然后将内存空间的指针返回给你。访问堆内存上的数据,都是要通过指针,找到指向的内存,然后再读取内存中的数据。 哪些数据是存在栈上,哪些是存在堆上 let x = 10; let y = "hello"; let c = 'A'; let x2 = x; 像上面这些简单的数据类型,都是存在于栈上,对于 Rust 而言,整型,浮点型,布尔型,字符型等,都是存在于栈上。而对于Rust的 String,这种可变大小的数据类型,是存在于堆上的。看下面的代码 let s1 = String::from("hello"); let s2 = "hello".to_string(); 上面的代码 from 就是从 &str 创建一个 String 类型,两行代码是两种方式实现同样的效果。一个 String 在内存中的形式如下图...

March 29, 2020 · 1 min · 猫猫

Rust 基础 | 基本语法

1. 变量和可变性 Rust 定义一个变量默认是不可变的,这一点与其他编程语言不同。 let x = 10; x = 11; // 这里会编译出错, 如果要定义可变量,需要使用 mut let mut x = 10; x = 11; 用 let 先定义一个变量,再次用 let 定义一个变量,会将之前的变量覆盖,虽然同名,但也不是原来的变量了,这叫做 Shadowing let x = 10; let x = "Hello"; 常量的定义使用 const,常量是永远不会变的量,常量必须在定义的时候注明数据类型 const MAX_SCORE: i32 = 10000; 2. 标量数据类型 Rust 数据类型可以分为标量类型和复合类型,标量类型有四种 整型,浮点型,布尔型,字符型。而整型分为 8位、16位、32位、64位、128位,动态长度这几种,每一种又分为有符号型,无符号型。浮点数分为 32位、64位。 关键字: 整型有符号型: i8 i16 i32 i64 i128 isize 整型无符号型: u8 u16 u32 u64 u128 usize 整型默认类型为 i32。注意 isize usize 的长度是动态的,如果运行程序的计算机是32位的,则为32位,如果计算机为64位的,则为64位。另外,在Rust中遍历集合时,常用 isize 和 usize。...

March 28, 2020 · 3 min · 猫猫

Unity Shader | 半兰伯特、高光反射、BlinnPhone

写 Shader,到底是在做什么?Shader 就像一个拥有魔法的黑箱,我们最终从它那里得到的,是像素的颜色。而得到的是什么样的像素颜色,则取决于我们丢进黑箱里的是什么东西,以及使用的魔法咒语是什么。丢进同样的东西,使用不同的魔法咒语,得到的像素颜色也是不一样的。输入顶点的坐标,光线方向,以及自定义的颜色,使用不同的计算方式,得到的像素颜色是不一样的。使用同样的计算方式,丢进一个纯色颜色值,和丢进一张纹理,得到的像素,也是不一样的。 如果把整个 Shader 比作一个函数,那么顶点位置,自定义的颜色值,纹理,灯光等这些就相当于函数的输入参数,而 Shader 中写的各种计算,用的各种算法,都是对输入的那些参数进行操作,而最终生成的,就是颜色值,也就是相当于函数的返回值。把 Shader 想的简单一点,就是输入需要的东西,进行计算,得到像素值。 这篇博客,还是关于光照模型的,接下来我们首先总结一下常用的光照模型。 Lambert (兰伯特) 光照模型 在之前的博客中说了漫反射的实现,实现漫反射用到的模型,叫做 Lambert (兰伯特) 光照模型。Lambert 实现出来的效果,一旦入射光向量与材质表面的角度大于90度,那么得到的漫反射颜色就会全部变为黑色,没有任何明暗变化效果。 Lambert 光照模型公式: 最终颜色 = 直射光颜色 * 漫反射颜色 * max(0, dot(光源方向, 法线方向)) 其中,直射光颜色,漫反射颜色,都是我们自定义的变量。 Half Lambert (半兰伯特) 光照模型 Half Lambert 是在 Lambert 模型的基础上,做了微调,也就是将光源方向与法线方向的点乘结果,从原来[-1, 1],映射为 [0, 1],这样原来背光面,也会有明暗效果。 Half Lambert 光照模型公式: 最终颜色 = 直射光颜色 * 漫反射颜色 * (dot(光源方向, 法线方向) * 0.5 + 0.5) Specular (高光反射) 这里的高光反射使用了 Phone 模型,的原理很简单,想象一束光射向某个点,然后反射出去,我们的眼睛同样看向那个点,当我们的眼睛看向那个点的方向,与光线反射的方向,越接近时,进入我们眼睛的反射光则越多,也就是更亮。看下面的图 很明显,当视野方向与光的反射方向夹角越小时,也就是说进入眼睛的光越多,所以那个点也就会越亮,这就是高光反射的原理。所以高光反射,实现起来也就很简单了,只要拿到视野方向,拿到直射光的反射方向,就可以求出最终的颜色值。 Specular 光照模型公式: 最终颜色 = 直射光颜色 * 反射光颜色 * pow(max(0, dot(反射光方向, 视野方向)), 光泽度(gloss)) + 漫反射颜色 + 环境光颜色...

March 19, 2020 · 4 min · 猫猫

Unity Shader | 光照模型和漫反射

在之前的文章中写的Shader,呈现出来的物体样子是一个平面2D的状态,即使物体是3D的,那是因为,我们还没有将灯光加入到Shader的运算中。现在,我们将介绍灯光相关的东西,最后呈现出和 Unity Diffuse Shader 一样的效果。 什么是光照模型 光照模型,简单理解就是一种运算,或者说一个公式,计算的结果,决定了一个点受到光照时,所表现出来的效果。例如,光照在木板上,和照在一面镜子上,我们所看到的效果是不一样的,照在镜子上,很大一部分光会被镜子反射,而木板,却不会反射那么多光。 进入摄相机的光线分类 在游戏中,我们可以将进入摄相机的光分为 高光反射、漫反射、自发光等。像上面说的镜子反射了大部分光,就是高光反射,现实中比较光滑的表面,受到光照时,都会产生这种效果,很亮。而光线照在木头上,就是漫反射,其实是木头先吸收了光,然后向周围散射出去,这个就不会很亮。而自发光,就是字面意思,自身是一个发光体。这里大概知道这些词是什么就可以,不必深究里面的原理。 这一篇博客,接下来我们将在Shader中实现一下漫反射。实现漫反射,可以在顶点函数中,这叫做逐顶点光照。也可以在片元函数中实现,这叫做逐片元光照。在顶点函数中实现,也就是对每一个顶点都进行一次光照的计算,而在片元函数中也就是对每一像素执行光照计算,所以,在片元函数中实现相对来说要更耗费一点性能。 在顶点函数中实现漫反射 漫反射的计算公式是 最终颜色=直射光颜色 * max(0, dot(光线,法线)),也就是使用 Directional Light 的颜色 乘 光线发射方向 与顶点法线方向的夹角,dot函数就是点乘,结果就是夹角。有一点要注意的是,dot中的 光线 和 法线 都是单位向量,也就是我们要对其进行标准化。max函数是取最大值,也就是说,如果dot计算出来的结果小于0,那就取0。 看下面的代码,注意看注释,从上往下每一个注释都要看 Shader "iMoeGirl/04-DiffuseVertex" { SubShader { Pass { // 要使用光照,首先要定义一下LightMode,这里我们使用ForwardBase, // 这里先不用管意思,只要照着写上就行 Tags { "LightMode" = "ForwardBase" } CGPROGRAM // 这里我们将 Unity 一些预定义的Shader代码包含进来, // 里面有我们需要的东西,场景中第一个Directional Light的信息(后面用来做计算) #include "Lighting.cginc" #pragma vertex vert #pragma fragment frag // 根据共识,要计算最终顶点的颜色,需要法线数据,所以这里将法线从Application传到顶点处理函数中 struct a2v { float4 vertex: POSITION; float3 normal: NORMAL; // NORMAL就是法线语义,之前的文章说过 }; struct v2f { float4 position: SV_POSITION; fixed3 color : COLOR; // 这个颜色就是在顶点函数中计算完的顶点的漫反射颜色,传到片元函数中 }; // 把光照的计算放在顶点函数中,所以叫做顶点光照 v2f vert(a2v v) { // 定义一个数据传送结构体(传送到片元函数中) v2f f; f....

March 15, 2020 · 2 min · 猫猫

Unity UGUI RGB通道分离抖动

今天我们要在 UGUI 上实现图片RGB通道分离抖动效果,先看最终效果图 ...

March 8, 2020 · 2 min · 猫猫

Unity Shader | 使用Struct传递数据

上一篇博客 说了在 CGPROGRAM 中写代码、顶点处理函数、片元处理函数、以及在两个函数之间传递简单的数据、从 ShaderLab 属性到CG数据类型之间的联系等。这一篇博将稍详细一点说一下 Shader 的基本知识,以及在顶点和片元函数之间传递更多的数据。 Shader的基本理解 简单来说,Shader 决定了一个模型最终呈现在屏幕上的样子。一个模型由很多顶点构成,而每一个顶点,都会经过 Shader 中的顶点处理函数,这个过程,就是从应用将数据传递到顶点处理函数,顶点函数需要将顶点从模型空间转换到屏幕空间,或者说是裁剪空间,也可以简单理解为从 3 维空间转换到屏幕上的 2 维空间。在这个过程中,还可以做一些其他对顶点的操作。 顶点数据经过顶点处理函数处理后,接下来就返回,然后传给片元处理函数,到了这一步,面对的,就是像素,也就是每一个像素的颜色值。在这里,可以根据自己的需求,对每一个像素做处理,例如做高斯模糊,RGB通道分离,等等,各种各样的效果。 向 Shader 传递更多的数据 在之前的博客文章中,我们只是将顶点的坐标传给了Shader,但是我们还需要其他的数据,例如法线,例如切线,纹理坐标等等。接下来,我们将使用结构体来存储要传递的数据,看下面的代码 Shader "iMoeGirl/03-Shader" { Properties { _MainColor("颜色类型", Color) = (1,1,1,1) } SubShader { Pass { CGPROGRAM float4 _MainColor; #pragma vertex vert #pragma fragment frag // 这里定义一个结构体,封装需要从应用传到顶点函数的数据 struct a2v { float4 vertex : POSITION; // 顶点坐标 float3 normal : NORMAL; // 顶点法线 float4 textcoord : TEXCOORD0; // 第一套纹理坐标(可以有多套) }; // 这里定义另一个结构体,封装从顶点函数传到片元函数的数据 struct v2f { float4 position : SV_POSITION; float3 temp : COLOR0; }; // 顶点处理函数,传入的是a2v结构体,返回的是要传到片元函数的v2f结构体 v2f vert(a2v v){ v2f result; result....

March 7, 2020 · 1 min · 猫猫

Unity Shader | 属性、顶点与片元函数

上一篇博客 介绍了Shader的基本结构,这里我们继续来说Shader的编写,也就是要在 CGPROGRAM 中写代码。首先我们把之前的Shader结构代码复制过来。 Shader "iMoeGirl/MyShader" { // Shader 名字 // 这里定义一些属性,可以显示在UI面板上用于调节 Properties { // 属性名("Inspector面板上显示出来的属性名", 属性类型) = 默认值 _Color("颜色类型", Color) = (1,1,1,1) _Vector("向量类型", Vector) = (1, 2, 3, 4) _Int("整型", Int) = 11111 _Float("浮点型", Float) = 12.11 _Range("范围类型", Range(100, 1000)) = 128 _Tex2D("贴图类型", 2D) = "white"{} _Cube("立方体贴图类型", Cube) = "white"{} _Tex3D("3D纹理", 3D) = "white"{} } // 子 Shader,可以写多个,显卡运行时, // 从第一个SubShader开始,如果第一个里面的效果都支持,则使用第一个, // 如果发现这个SubShader里面某些效果不支持,则自动运行下一个SubShader SubShader { // 至少有一个Pass,相当于一个方法 Pass { // 在Pass块里写Shader代码 CGPROGRAM // 使用 CG语言编写Shader ENDCG } } // 如果发现所有的SubShader都不支持,则使用Fallback,相当于后备方案 Fallback "VertexLit" } 怎样使用 Properties 中定义的属性 Unity3D定义Shader属性所使用的语法,和CG所使用的说法是不一样的,所以我们要在一个Pass中使用Properties中定义的属性,需要在Pass中再以CG的语法再写一遍,其实就是变量名相同,而数据类型不同,在Shader在编译的时候,就会自动将两个变量关联起来。看下面的代码...

March 2, 2020 · 2 min · 猫猫

Unity Shader | 基础

MeshFilter 存储一个Mesh(网格,模型的网格,模型的三角面顶点信息) MeshRenderer 用于渲染一个物体的外观,数据来源于MeshFilter Material 材质包含两部分,贴图和Shader OpenGL DirectX 直接与显卡交互图形渲染库,可以理解为应用程序与显卡之间的桥梁,为应用程序提供一些渲染接口,用于渲染。 Shader Shader可以理解为是一种渲染命令,由opengl或DX进行解析,用于控制图形的渲染。 GLSL/HLSL/CG shader编程语言,GLSL面向OpenGL,HLSL面向DirectX,CG是Nvidia公司出的,跨平台的shader编程语言。 ShaderLab 我们在Unitiy中写Shader用的语言是ShaderLab,可以理解为Unity为了方便使用者写Shader而创造的一种新的Shader语言,最后其实都会在底层被翻译成GLSL或HLSL或CG。 Unity中的Shader分类 Shader的中文名叫做着色器 表面着色器,Surface Shader 顶点/片元着色器,Vertex/Fragment Shader 固定功能着色器,Fixed function Shader (在现代硬件上基本已被弃用) 表面着色器可以理解为是对顶点/片元着色器的一种封装,它帮我们处理了很多渲染上比较麻烦的事情。而顶点/片元着色器就相对更灵活一些,也就是说很多东西要自己处理,相对来说要写的代码更多一些。进一步讲,使用顶点/片元着色器能实现的效果,使用表面着色器并不一定能实现,或者说,并不一定那么方便地实现。 Unity Shader 结构 Shader "iMoeGirl/MyShader" { // Shader 名字 Properties { // 这里定义一些属性,可以显示在UI面板上用于调节 } SubShader { // 子 Shader,可以写多个,显卡运行时, // 从第一个SubShader开始,如果第一个里面的效果都支持,则使用第一个, // 如果发现这个SubShader里面某些效果不支持,则自动运行下一个SubShader } // 如果发现所有的SubShader都不支持,则使用Fallback,相当于后备方案 Fallback "VertexLit" } Unity Shader 属性类型 Shader "iMoeGirl/MyShader" { // Shader 名字 // 这里定义一些属性,可以显示在UI面板上用于调节 Properties { // 属性名("Inspector面板上显示出来的属性名", 属性类型) = 默认值 _Color("颜色类型", Color) = (1,1,1,1) _Vector("向量类型", Vector) = (1, 2, 3, 4) _Int("整型", Int) = 11111 _Float("浮点型", Float) = 12....

February 24, 2020 · 1 min · 猫猫

SVN Cleanup错误 Faild to run the wc db work queue associated with

今天svn更新时,因为一个文件问题,导致了 Faild to run the wc db work queue associated with.... 的错误,无法 Cleanup,也无法 Update,无法 Revert,现在记录一下解决方法,很简单,只需要三个 sqlite 命令即可。 这个过程要用到 sqlite3 的命令行工具,可以在官网下载。找到 Precompiled Binaries for WIndows 中的 sqlite-tools-win32-x86-xxxxxx.zip,点击下载即可,xxxxxx 是指版本号,下载官网上的最新版本即可。 要在 cmd 中运行,还要将下载并解压后的 sqlite 目录加到环境变量的 Path 中,这样就可以直接在 cmd 中使用 sqlite 命令了。 下面是解决问题的流程 在 cmd 中通过 cd 命令进入到 svn 工程中的 .svn 目录中 执行命令 sqlite3 wc.db "select * from work_queue" 查看有哪些文件报错 执行命令 sqlite3 wc.db "delete from work_queue" 执行命令 sqlite3 wc.db "delete from wc_lock" 好了,现在工程已经恢复正常。

February 13, 2020 · 1 min · 猫猫

使用 Python 去掉 Kindle PC 复制时自动添加的内容

在使用 Kindle PC 软件看英文电子书时,常常会配合 CopyTranslator 软件做剪切板内容翻译,但是最近发现 Kindle PC 在复制内容时,会在剪切板中自动附加上书籍的内容,就像下面这样 There’s nothing wrong with pursuing venture funding and attempting to grow fast like eBay Walling, Rob; Taber, Mike. Start Small, Stay Small: A Developer's Guide to Launching a Startup (Kindle位置28). The Numa Group, LLC. Kindle 版本. 上面的内容,第一行是我要复制的内容,Walling, Rob; Taber, Mike. Start Small, Stay Small: A Developer's Guide to Launching a Startup (Kindle位置28). The Numa Group, LLC. Kindle 版本. 是Kindle PC自动附加的书籍信息内容,这一点很烦人。 要解决这个问题其实很简单,只要监控剪切板变化时,将剪切板中的字符串,去掉自动附加的内容,然后将新的字符串替换回剪切板即可。接下来我们使用Python来做这件事情,代码只需要很少几行即可。...

February 11, 2020 · 1 min · 猫猫