开荒!Unity3D!
前言
行为受限于物理学的发展,思想却不是。
赋予事物以你自己的意义。
哲学三大问
Unity是什么
-
游戏引擎,Unity本质是一个游戏引擎,但Unity出色的功能使其并不局限于游戏开发,除游戏开发外,美术、建筑、汽车设计、影视等在内的所有创作者,皆会借助Unity将创意变成现实。Unity平台提供一整套完善的软件解决方案 ,可用于创作、运营和变现任何实时互动的2D和3D内容,支持平台包括手机、平板电脑、PC、游戏主机、增强现实和虚拟现实设备。
——百度百科 摘抄时间 2021.5.15
-
奇思妙想实现器,Unity强大的物理引擎包容各种奇思妙想,2D、3D开发支持,满足了绝大多数开发需要。丰富多彩的材质满足了已知的、未知的、想象的所有内容。在Unity里,你可以构造你想象中的世界,你就是这个世界规则的创建者。
——初入Unity,Unity初印象 2021.5.15
为什么用Unity
-
Unity具备的优点:
- 支持多种格式导入(3dsMax, Maya, Lightwave, Collade等文档)
- 高性能的灯光照明系统
- AAA级图像渲染引擎
- NVIDIA专业的物理引擎
- 友善的专业开发工具
- 高效率的路径寻找与人群仿真系统
- 逼真的粒子系统
- 强大的地形编辑器
- 市场空间(多平台支持)
- 智能界面设计,细节凸显专业
- 单机及在线游戏发布
- TeamLicense协同开发系统
- Substance高写真动态材质模块
- 可视化脚本语言u
——摘抄自百度 2021.5.15
-
你也可以用UE4(滑稽)
——整活儿少年 2021.5.15
怎么用Unity
Unity的具体使用方式见下一章节
- Unity引擎
- Unity开发语言(C#)
- Unity项目开发
- …
Unity入门
Unity引擎
-
Unity下载、安装、使用
-
引擎基本功能
Unity脚本语言(C#)
-
C#语法
请移步隔壁文章
Unity引擎入门
实践是检验真理的唯一标准
实践跟随:https://zhuanlan.zhihu.com/p/151238164
-
1.游戏对象和脚本
-
2.构建视图(Visualizing Math)
通过若干小方块,模拟出数学函数。最终实现小方块个数可配置;小方块在不同的坐标,表现出不同的颜色;小方块的Y坐标会随横坐标X和时间t变换,形成一一对应关系。最终实现了sin函数的展示。
预制体
面向对象的思想在编程中十分重要,我们可以把预制体理解为一个对象,它可以在被需要的时候再进行实例化,当然也可以在Scene场景中直接创建;它和对象一样,当它的属性发生更改后,实例化出来的对象中的内容也会更改。
当前我们需要在场景中建立很多cube小方块,用于模拟单个像素。如果我们每次都在场景里手动添加,那么这个操作将十分繁琐,而且不利于扩展(比如有些时候我希望分辨率是10个cube,有些时候我希望分辨率是100个cube),因此,这些cube是需要动态生成的。在这种情况下使用预制体,并在实例化预制体时,根据相应逻辑为预制体赋予初值,就可以快速在场景中得到我们想要的内容。
表面着色器
Unity提供了一个框架来快速生成执行默认照明计算的着色器,我们可以通过调整某些值来影响该着色器,这种着色器称为表面着色器,然而,它仅适用于默认渲染管道。
Unity着色器语法(简)
这里仅简要介绍本项目中会用到的一些内容。本项目编写的表面着色器是为了让cube方块在不同的坐标点有不同的颜色,思路是将物体坐标与物体颜色关联起来。物体坐标是三个值的,物体颜色抛开alpha通道不计,也可认为是三个值的。因此我们直接将物体世界坐标赋值给rgb值即可。
本项目着色器代码如下。
//shader关键字 + "字符串" (用于定义着色器的菜单项) Shader "Graph/PointSurface" { //以下都是着色器内容的代码块 //写入此处的资源 都可以在编辑器中显示 Properties { _Smoothness ("Smoothness", Range(0,1)) = 0.5 //_Smoothness ("Smoothness", Range(0,1)) = 0.5; 不可以加 分号 !!!! } SubShader //着色器的子着色器, 由SubShader关键字定义,可以有多个 { //Tags { "RenderType"="Opaque" } //LOD 200 //CG和HLSL的混合体(两种着色器语言)编写的代码段, //开头, 结尾 需要用CGPROGRAM 和 ENDCG 关键字括起来 CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types //需要的第一个语句是编译器指令 称为编译指示 //写为 #pragma ,后接指令 //此处使用的是 指示着色器编译器生成具有标准照明 并且 完全支持阴影的表面着色器 //ConfigureSurface 是指用于配色着色器的方法, 我们需要创建该方法 #pragma surface ConfigureSurface Standard fullforwardshadows // Use shader model 3.0 target, to get nicer looking lighting //遵循 #pragma target 3.0 指令, 该指令为着色器的target级别和质量设置了最小值 #pragma target 3.0 //为配置函数定义输入结构, 需要被写为 struct Input, 后跟代码块,后跟分号(;) //在代码块内,声明一个 结构域 ,特别是float3 worldPos, 它包含 渲染内容 的世界位置. //float3 类型是 Vector3结构 的等效着色器 struct Input { float3 worldPos; }; float _Smoothness; //定义 ConfigureSurface 方法, 在着色器中被称为函数 //这是带两个参数的, 返回是 void 的方法 //第一个参数是刚才定义的 Input类型的输入参数 //第二个参数是 表面配置数据 , 类型为SurfaceOutputStandard //第二个参数需要在前面加 inout 关键字, 表明它用于函数的结果 void ConfigureSurface(Input input, inout SurfaceOutputStandard surface){ //调整点的颜色, 需要修改 surface.Albedo //反照率 和 世界位置 都是三个组成部分, 所以可以直接把它用在反照率上 //此时, (r, g, b) 对应 世界坐标的 (x, y, z) //由于此时的x定义域在[-1, 1], 在负数时, r对应的颜色无意义, 因此颜色减半后 + 0.5 ,让坐标位置能够适应颜色 //坐标当前区域 [-1, 1] * 0.5 + 0.5 后, 得到颜色区域 [0, 1] //surface.Albedo = input.worldPos * 0.5 + 0.5; //当前坐标 z 一直为 0, 使得蓝色分量一直接近0.5, //可以通过设置反照率时,仅设置 r g 通道, 来消除蓝色, 此时的b 即默认0 //surface.Albedo.rg = input.worldPos.xy * 0.5 + 0.5; //由于是cube立方体在移动,因此移动到最高点时,会有部分超出[-1,1]的范围 //因此我们要正确钳位颜色, 确保颜色值都在0-1范围内 //可以把生成的颜色传给 saturate函数, 这个函数可以把所有组件 钳位为0-1范围内 //这是着色器中常见操作,称为饱和度 surface.Albedo.rg = saturate(input.worldPos.xy * 0.5 +0.5); //调整顺滑度, 越高越金属, 越低越磨砂 surface.Smoothness = _Smoothness; } ENDCG } //向标准的漫反射着色器添加一个后备 FallBack "Diffuse" }
-
3.数学表面(Sculpting with Numbers)
在上一小节,将cube作为一个像素单元,完成了2d视图下的数学绘图。本小节将延续上一小节,显示更多更复杂的函数,同时将维度从2维提升至3维。
数学的表达
老实说,这一节的内容,更多的是在讲数学内容。
在上一节中,我们通过不同的x,获得不同的y,实现了从x到y的映射。在本节,我们将实现:
①从(x,z)到y的映射,当然,如果希望画面动起来,可以加入时间t。
②离开平面网格的限制,使用三维函数。
对于①部分的内容,其实是上一小节中,对于维度的提升。第②部分的内容才是本节中的重要部分。
第②部分的内容,舍弃了使用X和Z来定义Y的方式,这个方式虽然可以描述多种表面函数,但是存在局限性:它们始终与XZ平面链接,即相同的X,Z下,只能出现一个Y值,这导致了这样的描述方式,无法描述球面、圆环等存在坡度垂直XZ平面的曲面,因此,为了能够描述更多的表面,我们的函数不仅需要输出Y,还必须输出X、Z。
之前的函数都是输出1D的值(即只输出了一个轴的值),我们的函数如果能够输出3D位置(即能够输出XYZ的值),那么我们就可以用它来创建任意的表面了,这样输出3D位置的函数我们把它叫做三维函数。
比如:
f(u,v) = [u, 0, v],描述了XZ平面;
f(u,v) = [u, v, 0],描述了XY平面;
此时输入的参数不再对应最终的坐标(比如以前输入的x1,则最终X坐标的值就是x1),而是通过一定的转换关系(各类曲面方程的描述),来得到最终的坐标值(x,y,z),以此达到描述所有面的目的(只要能写出这个面的方程hhhhhhh)。
同参数函数封装
使用委托将所有的同参、同类型函数都封装到一个方法里,需要用的时候直接调用该方法,并传入要使用函数的名字,通过这样的封装让函数的使用更方便,代码更简洁。
//声明一个委托,想用这个委托,就要给我u,v,t public delegate Vector3 Function(float u, float v, float t); //委托数组,把那些方法都丢过来 //务必记得顺序和 枚举 里的顺序一样,方便后续的逻辑处理 static Function[] functions = { Wave, MultiSine, Ripple, Sphere, Torus }; //获取对应方法 的方法 //输入不同的函数名字,可以拿到不同的对应的函数去处理 public static Function GetFunction(FunctionName name) { return functions[(int)name]; } //使用枚举,记录每个方法,记住和委托数组里的位置一一对应 //主要是为了方便自己在Inspector面板换函数(懒且勤快) //枚举里的值都是写了的方法 public enum FunctionName{Wave, MultiSine, Ripple, Sphere, Torus} //有好多符合这个委托的方法 //这些方法在枚举里也记得写一下,枚举里的顺序和委托数组里的顺序务必相同!(多次强调) //xxx(float u, v, t){ ...} //...
3D颜色
本节内容需要表面着色器在x,y,z三个轴的方向都有颜色的变化,因此这里我们需要将上一节封禁的z放出来。在表面着色器的代码中修改以下的内容:
//原↓ //surface.Albedo.rg = saturate(input.worldPos.xy * 0.5 +0.5); //修改为↓ ,目的是让blue的值受z值的影响 surface.Albedo = saturate(input.worldPos * 0.5 +0.5);
-
4.测量性能
本节内容主要是对Unity运行的监视,分别在编辑器播放模式和build下,对程序运行性能的监视。还导入了UI内容,通过UI面板,在游戏运行时的右上角显示了FPS、MS内容。接着改造了上一小节的内容:间隔一段时间随机显示下一条函数,且加入了插值,让函数变化平滑。
线性插值
Vector.Lerp
在两个数之间产生一个直线的恒速转换。返回的值在[a,b],t=0,返回a,t=1,返回b。
这是最普通的线性插值,也是钳制性的线性插值,最终的返回值被限定在a,b之间。
非钳制性的线性插值
Vector3.LerpUnclamped
这个函数的返回值部分会超过a和b的范围。
平滑阶梯函数
Smoothstep
公式定义:
float Smoothstep(float t1, float t2, float x) { x = clamp((x - t1) / (t2 - t1), 0.0, 1.0); return x * x * ( 3 - 2 * x); }
其中,clamp函数定义:
//输入x, min, max,如果x大于max,返回manx;如果x小于min,返回min; //如果x小于max,大于min,则返回x,表明x在(min, max)中 template<class T> T clamp(T x, T min, T max) { if(x > max) { return max; } if(x < min) { return min; } return x; }
Smoothstep部分数学公式: \(3 * x^2 - 2 * x^3\)
-
5.计算着色器
-
6.Jobs
-
7.有机品种